// ==UserScript==
// @name            Discord Message to Webhook
// @description     Combined tool for viewing JSON data of Discord messages and bulk deletion
// @version         1.0.2
// @author          Original scripts by various authors, edited by jirachi
// @match           https://*.discord.com/app
// @match           https://*.discord.com/channels/*
// @match           https://*.discord.com/login
// @license         none
// @grant           none
// ==/UserScript==

(function () {
    'use strict';

    // --- Configuration ---
    let logFn = null;
    let observerThrottle = null;

    // --- CSS ---
    const themeCss = `
/* JSON Viewer Box */
#discord-message-json-display {
    position: fixed;
    top: 50px;
    right: 50px;
    width: 450px;
    height: 300px;
    background-color: var(--background-secondary);
    border: 1px solid var(--background-tertiary);
    border-radius: 5px;
    z-index: 1000;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    font-size: 12px;
    color: var(--text-normal);
    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
    resize: both;
    overflow: auto;
    min-width: 250px;
    min-height: 150px;
}
#discord-message-json-display .header {
    padding: 10px;
    background-color: var(--background-tertiary);
    cursor: grab;
    font-weight: bold;
    border-bottom: 1px solid var(--background-tertiary);
    display: flex;
    justify-content: space-between;
    align-items: center;
    user-select: none;
}
#discord-message-json-content {
    flex-grow: 1;
    padding: 10px;
    margin: 0;
    overflow: auto;
    white-space: pre-wrap;
    word-wrap: break-word;
    background-color: var(--background-primary);
}
#discord-message-json-display::-webkit-resizer {
    background-color: var(--brand-experiment);
}
.format-toggle-container {
    display: flex;
    align-items: center;
    font-weight: normal;
    font-size: 10px;
    margin-right: 10px;
    cursor: pointer;
}
.format-toggle-container input {
    margin-right: 5px;
    cursor: pointer;
}

/* Custom JSON syntax highlighting */
.json-string { color: #a8ff60; }
.json-number { color: #d8a0df; }
.json-boolean { color: #b285fe; }
.json-null { color: #70a0d8; }
.json-key { color: #f08d49; }
.json-punctuation { color: #7a82da; }
`;

    // --- HTML Elements ---
    // SVG Data for the JSON icon
    const jsonSvgIcon = `<svg width="18px" height="18px" viewBox="-10 -5 1034 1034" xmlns="http://www.w3.org/2000/svg">
   <path fill="currentColor" d="M482 226h-1l-10 2q-33 4 -64.5 18.5t-55.5 38.5q-41 37 -57 91q-9 30 -8 63t12 63q17 45 52 78l13 12l-83 135q-26 -1 -45 7q-30 13 -45 40q-7 15 -9 31t2 32q8 30 33 48q15 10 33 14.5t36 2t34.5 -12.5t27.5 -25q12 -17 14.5 -39t-5.5 -41q-1 -5 -7 -14l-3 -6l118 -192 q6 -9 8 -14l-10 -3q-9 -2 -13 -4q-23 -10 -41.5 -27.5t-28.5 -39.5q-17 -36 -9 -75q4 -23 17 -43t31 -34q37 -27 82 -27q27 -1 52.5 9.5t44.5 30.5q17 16 26.5 38.5t10.5 45.5q0 17 -6 42l70 19l8 1q14 -43 7 -86q-4 -33 -19.5 -63.5t-39.5 -53.5q-42 -42 -103 -56 q-6 -2 -18 -4l-14 -2h-37zM500 350q-17 0 -34 7t-30.5 20.5t-19.5 31.5q-8 20 -4 44q3 18 14 34t28 25q24 15 56 13q3 4 5 8l112 191q3 6 6 9q27 -26 58.5 -35.5t65 -3.5t58.5 26q32 25 43.5 61.5t0.5 73.5q-8 28 -28.5 50t-48.5 33q-31 13 -66.5 8.5t-63.5 -24.5 q-4 -3 -13 -10l-5 -6q-4 3 -11 10l-47 46q23 23 52 38.5t61 21.5l22 4h39l28 -5q64 -13 110 -60q22 -22 36.5 -50.5t19.5 -59.5q5 -36 -2 -71.5t-25 -64.5t-44 -51t-57 -35q-34 -14 -70.5 -16t-71.5 7l-17 5l-81 -137q13 -19 16 -37q5 -32 -13 -60q-16 -25 -44 -35 q-17 -6 -35 -6zM218 614q-58 13 -100 53q-47 44 -61 105l-4 24v37l2 11q2 13 4 20q7 31 24.5 59t42.5 49q50 41 115 49q38 4 76 -4.5t70 -28.5q53 -34 78 -91q7 -17 14 -45q6 -1 18 0l125 2q14 0 20 1q11 20 25 31t31.5 16t35.5 4q28 -3 50 -20q27 -21 32 -54 q2 -17 -1.5 -33t-13.5 -30q-16 -22 -41 -32q-17 -7 -35.5 -6.5t-35.5 7.5q-28 12 -43 37l-3 6q-14 0 -42 -1l-113 -1q-15 -1 -43 -1l-50 -1l3 17q8 43 -13 81q-14 27 -40 45t-57 22q-35 6 -70 -7.5t-57 -42.5q-28 -35 -27 -79q1 -37 23 -69q13 -19 32 -32t41 -19l9 -3z"/>
</svg>`;

    // --- JSON Viewer Variables ---
    let jsonDisplayBox = null;
    let isDragging = false;
    let dragOffsetX, dragOffsetY;
    let isScriptActive = true;
    let currentMessage = null;
    let isWebhookFormat = false;

    // --- Utility Functions ---
    const $ = s => document.querySelector(s);

    const log = {
        debug() { return logFn ? logFn('debug', arguments) : console.debug.apply(console, arguments); },
        info() { return logFn ? logFn('info', arguments) : console.info.apply(console, arguments); },
        verb() { return logFn ? logFn('verb', arguments) : console.log.apply(console, arguments); },
        warn() { return logFn ? logFn('warn', arguments) : console.warn.apply(console, arguments); },
        error() { return logFn ? logFn('error', arguments) : console.error.apply(console, arguments); },
        success() { return logFn ? logFn('success', arguments) : console.info.apply(console, arguments); }
    };

    function hslaToHex(hsla) {
        // Handle the specific calc variable case by replacing it with 100%
        const processedHsla = hsla.replace(/calc\(var\(--saturation-factor,\s*1\)\s*\*\s*100%\)/g, '100%');
    
        // Parse the HSLA values
        const parts = processedHsla.match(/hsla?\((\d+),\s*(\d+(\.\d+)?%),\s*(\d+(\.\d+)?%),\s*(\d?(\.\d+)?)\)/);
        if (!parts) {
            // If parsing fails, return a default color or handle the error
            console.error("Failed to parse HSLA string:", hsla);
            return null; // Or return a default hex color like 0
        }
    
        const h = parseInt(parts[1], 10);
        const s = parseFloat(parts[2]) / 100;
        const l = parseFloat(parts[4]) / 100;
        const a = parseFloat(parts[7] !== undefined ? parts[7] : 1); // Default alpha to 1 if not present
    
        // Convert HSL to RGB
        let r, g, b;
    
        if (s === 0) {
            r = g = b = l; // Achromatic
        } else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1 / 6) return p + (q - p) * 6 * t;
                if (t < 1 / 2) return q;
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
                return p;
            };
    
            const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p = 2 * l - q;
    
            r = hue2rgb(p, q, h / 360 + 1 / 3);
            g = hue2rgb(p, q, h / 360);
            b = hue2rgb(p, q, h / 360 - 1 / 3);
        }
    
        // Convert RGB to Hex
        const toHex = x => {
            const hex = Math.round(x * 255).toString(16);
            return hex.length === 1 ? '0' + hex : hex;
        };
    
        const hex = `${toHex(r)}${toHex(g)}${toHex(b)}`;
    
        // Handle Alpha (optional, as webhook colors are typically RGB hex)
        // If you need to include alpha in the hex (RRGGBBAA), uncomment the following:
        /*
        const toHexAlpha = x => {
            const hexA = Math.round(x * 255).toString(16);
            return hexA.length === 1 ? '0' + hexA : hexA;
        };
        return parseInt(hex + toHexAlpha(a), 16);
        */
    
        return parseInt(hex, 16);
    }

    const setLogFn = (fn) => logFn = fn;

    function createElm(html) {
        const temp = document.createElement('div');
        temp.innerHTML = html;
        return temp.removeChild(temp.firstElementChild);
    }

    function insertCss(css) {
        const style = document.createElement('style');
        style.innerHTML = css;
        document.head.appendChild(style);
        return style;
    }

    // --- React Instance Finders ---
    function findReactInstance(element) {
        for (const key in element) {
            if (key.startsWith('__reactFiber$') || key.startsWith('__reactProps$')) {
                return element[key];
            }
        }
        return null;
    }

    function getMessageFromReactInstance(reactInstance) {
        if (!reactInstance) return null;

        let current = reactInstance;
        while (current) {
            if (current.memoizedProps && current.memoizedProps.message) {
                return current.memoizedProps.message;
            }
            current = current.return;
        }
        return null;
    }

    // --- JSON Conversion Function ---
    function convertMessageToWebhookJson(message) {
        if (!message) return null;

        const webhookJson = {};

        // Basic message properties
        if (message.content) {
            webhookJson.content = message.content;
        }
        // console.log(message);
        // Username and Avatar
        if (message.author) {
            webhookJson.username = message.author.username;
            if (message.author.avatar) {
                const avatarHash = message.author.avatar;
                const userId = message.author.id;
                const isGif = avatarHash.startsWith('a_');
                webhookJson.avatar_url = `https://cdn.discordapp.com/avatars/${userId}/${avatarHash}.${isGif ? 'gif' : 'png'}`;
            } else {
                // Default avatar if none is set
                const discriminator = message.author.discriminator;
                let defaultAvatarId;
                if (discriminator === '0') {
                    defaultAvatarId = (BigInt(message.author.id) >> 22n) % 6n;
                    webhookJson.avatar_url = `https://cdn.discordapp.com/assets/${defaultAvatarId}.png`;
                } else {
                    defaultAvatarId = parseInt(discriminator) % 5;
                    webhookJson.avatar_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarId}.png`;
                }
            }
        }

        // Embeds
        if (message.embeds) {
            
            webhookJson.embeds = message.embeds.map(embed => {
                const webhookEmbed = {};
                // console.log(embed);
                if (embed.rawTitle) webhookEmbed.title = embed.rawTitle;
                if (embed.title) webhookEmbed.title = embed.title;
                if (embed.rawDescription) webhookEmbed.description = embed.rawDescription;
                if (embed.description) webhookEmbed.description = embed.description;
                if (embed.url) webhookEmbed.url = embed.url;

                // Color
                // Original snippet with the added HSLA handling
                if (embed.color !== undefined) {
                    if (typeof embed.color === 'string') {
                        try {
                            // Check if the color string is an HSLA value
                            if (embed.color.startsWith('hsl')) {
                                const hexColor = hslaToHex(embed.color);
                                if (hexColor !== null) {
                                    webhookEmbed.color = hexColor;
                                } else {
                                    // Handle the case where HSLA parsing failed
                                    console.error("Could not convert HSLA color to hex:", embed.color);
                                    // Optionally set a default color or leave it undefined
                                    webhookEmbed.color = 0; // Example: set to black
                                }
                            } else {
                                // Assume it's a hex string if not HSLA
                                webhookEmbed.color = parseInt(embed.color.replace('#', ''), 16);
                            }
                        } catch (e) {
                            console.error("Failed to process embed color string:", embed.color, e);
                        }
                    } else if (typeof embed.color === 'number') {
                        webhookEmbed.color = embed.color;
                    }
                }

                if (embed.timestamp) webhookEmbed.timestamp = embed.timestamp;

                if (embed.footer) {
                    const webhookFooter = {};
                    if (embed.footer.text) webhookFooter.text = embed.footer.text;
                    if (embed.footer.icon_url) webhookFooter.icon_url = embed.footer.icon_url;
                    if (Object.keys(webhookFooter).length > 0) webhookEmbed.footer = webhookFooter;
                }

                if (embed.image) {
                    const webhookImage = {};
                    if (embed.image.url) webhookImage.url = embed.image.url;
                    if (Object.keys(webhookImage).length > 0) webhookEmbed.image = webhookImage;
                }

                if (embed.thumbnail) {
                    const webhookThumbnail = {};
                    if (embed.thumbnail.url) webhookThumbnail.url = embed.thumbnail.url;
                    if (Object.keys(webhookThumbnail).length > 0) webhookEmbed.thumbnail = webhookThumbnail;
                }

                if (embed.author) {
                    const webhookAuthor = {};
                    if (embed.author.name) webhookAuthor.name = embed.author.name;
                    if (embed.author.url) webhookAuthor.url = embed.author.url;
                    if (embed.author.icon_url) webhookAuthor.icon_url = embed.author.icon_url;
                    if (Object.keys(webhookAuthor).length > 0) webhookEmbed.author = webhookAuthor;
                }

                if (embed.fields && embed.fields.length > 0) {
                    webhookEmbed.fields = embed.fields.map(field => {
                        const webhookField = {};
                        if (field.name) webhookField.name = field.name;
                        if (field.value) webhookField.value = field.value;
                        if (field.inline !== undefined) webhookField.inline = field.inline;
                        return webhookField;
                    });
                }

                return webhookEmbed;
            }).filter(embed => Object.keys(embed).length > 0);
        }

        return webhookJson;
    }

    // --- JSON Viewer Functions ---
    function createJsonDisplayBox() {
        jsonDisplayBox = document.createElement('div');
        jsonDisplayBox.id = 'discord-message-json-display';
        jsonDisplayBox.style.display = 'none'; // Initially hidden
        
        const header = document.createElement('div');
        header.className = 'header';
        // Create a text node as the first child
        header.appendChild(document.createTextNode('Discord Message JSON Data'));

        const controls = document.createElement('div');
        controls.style.cssText = 'display: flex; align-items: center;';

        // Webhook Toggle
        const webhookToggleLabel = document.createElement('label');
        webhookToggleLabel.style.cssText = 'font-weight: normal; font-size: 10px; margin-right: 10px; display: flex; align-items: center; cursor: pointer;';
        webhookToggleLabel.textContent = 'Webhook Format';

        const webhookToggle = document.createElement('input');
        webhookToggle.type = 'checkbox';
        webhookToggle.style.cssText = 'margin-right: 5px; cursor: pointer;';
        webhookToggle.addEventListener('change', function() {
            isWebhookFormat = this.checked;
            if (currentMessage) {
                // Only update content, not the entire structure
                const jsonToDisplay = isWebhookFormat ? convertMessageToWebhookJson(currentMessage) : currentMessage;
                const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.';
                const contentElement = document.getElementById('discord-message-json-content');
                contentElement.textContent = jsonString;
                
                // Apply our own highlighting
                highlightJsonContent();
            }
            // Update header text but preserve the DOM structure
            const headerText = document.querySelector('#discord-message-json-display .header');
            const headerTitle = headerText.childNodes[0];
            headerTitle.textContent = isWebhookFormat ? 'Discord Webhook JSON Data' : 'Discord Message JSON Data';
        });

        webhookToggleLabel.prepend(webhookToggle);
        controls.appendChild(webhookToggleLabel);

        // Copy Button
        const copyButton = document.createElement('div');
        copyButton.style.cssText = 'font-weight: normal; font-size: 10px; margin-right: 10px; cursor: pointer; display: flex; align-items: center;';
        copyButton.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/></svg>';
        copyButton.innerHTML += '<span style="margin-left: 4px;">Copy</span>';
        
        copyButton.onclick = () => {
            const jsonContent = document.getElementById('discord-message-json-content');
            
            // Get the plain text content, not the highlighted HTML
            let textToCopy = '';
            if (currentMessage) {
                const jsonToDisplay = isWebhookFormat ? convertMessageToWebhookJson(currentMessage) : currentMessage;
                textToCopy = JSON.stringify(jsonToDisplay, null, 2);
            } else {
                textToCopy = jsonContent.textContent;
            }
            
            // Copy to clipboard
            navigator.clipboard.writeText(textToCopy)
                .then(() => {
                    // Visual feedback
                    const originalText = copyButton.querySelector('span').textContent;
                    copyButton.querySelector('span').textContent = 'Copied!';
                    setTimeout(() => {
                        copyButton.querySelector('span').textContent = originalText;
                    }, 1000);
                })
                .catch(err => {
                    console.error('Failed to copy JSON: ', err);
                });
        };
        
        controls.appendChild(copyButton);

        const closeButton = document.createElement('span');
        closeButton.style.cssText = 'margin-left: 10px; cursor: pointer; font-size: 16px; color: var(--interactive-normal);';
        closeButton.textContent = '✕';
        closeButton.onclick = () => {
            jsonDisplayBox.style.display = 'none';
        };

        controls.appendChild(closeButton);
        header.appendChild(controls);
        jsonDisplayBox.appendChild(header);

        const content = document.createElement('pre');
        content.id = 'discord-message-json-content';
        jsonDisplayBox.appendChild(content);

        document.body.appendChild(jsonDisplayBox);

        // Make the box draggable
        header.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return;
            isDragging = true;
            dragOffsetX = e.clientX - jsonDisplayBox.getBoundingClientRect().left;
            dragOffsetY = e.clientY - jsonDisplayBox.getBoundingClientRect().top;
            jsonDisplayBox.style.cursor = 'grabbing';
            document.body.style.userSelect = 'none';
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            jsonDisplayBox.style.left = (e.clientX - dragOffsetX) + 'px';
            jsonDisplayBox.style.top = (e.clientY - dragOffsetY) + 'px';
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
            if (jsonDisplayBox) jsonDisplayBox.style.cursor = '';
            document.body.style.userSelect = '';
        });
    }

    function highlightJsonContent() {
        const codeElement = document.getElementById('discord-message-json-content');
        if (!codeElement) return;
        
        // Get the text content
        const text = codeElement.textContent;
        
        // JSON syntax highlighting with colons preserved
        let highlighted = text.replace(
            /"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, 
            function (match) {
                let cls = 'json-number';
                if (/^"/.test(match)) {
                    if (/:$/.test(match)) {
                        cls = 'json-key';
                        // Don't remove the colon
                    } else {
                        cls = 'json-string';
                    }
                } else if (/true|false/.test(match)) {
                    cls = 'json-boolean';
                } else if (/null/.test(match)) {
                    cls = 'json-null';
                }
                
                return `<span class="${cls}">${match}</span>`;
            }
        );
        
        // Also highlight brackets and punctuation (but not colons as they're already handled)
        highlighted = highlighted.replace(/[{}\[\],]/g, function(match) {
            return `<span class="json-punctuation">${match}</span>`;
        });
        
        codeElement.innerHTML = highlighted;
    }

    function displayMessageJson(message) {
        if (!jsonDisplayBox) {
            createJsonDisplayBox();
        }

        currentMessage = message;

        let jsonToDisplay = null;
        if (currentMessage) {
            if (isWebhookFormat) {
                jsonToDisplay = convertMessageToWebhookJson(currentMessage);
            } else {
                jsonToDisplay = currentMessage;
            }
        }

        const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.';
        const contentElement = document.getElementById('discord-message-json-content');
        contentElement.textContent = jsonString;
        
        // Apply our custom syntax highlighting
        highlightJsonContent();
        
        jsonDisplayBox.style.display = 'flex';
    }

    // Handle message clicks for JSON viewing
    function handleMessageClick(event) {
        if (!isScriptActive) return;

        const target = event.target;
        const messageElement = target.closest('[id^="message-"]');

        if (messageElement) {
            // Skip if clicking interactive elements
            const interactiveElements = target.closest('a, button, .markup-2BOw-j');
            if (interactiveElements && !interactiveElements.classList.contains('embedWrapper-lXp9gn')) {
                return;
            }

            const reactInstance = findReactInstance(messageElement);
            if (reactInstance) {
                const message = getMessageFromReactInstance(reactInstance);
                if (message) {
                    displayMessageJson(message);
                    return;
                }
            }
            
            // log.warn("Could not find React instance for message element.");
            if (jsonDisplayBox && document.getElementById('discord-message-json-content')) {
                document.getElementById('discord-message-json-content').textContent = 'Could not retrieve message data.';
                currentMessage = null;
            }
        }
    }

    // --- Button Creation and Mounting ---
    let jsonViewerBtn = null;

    function createJsonViewerButton() {
        // Create button container
        jsonViewerBtn = document.createElement('div');
        jsonViewerBtn.id = 'json-viewer-btn';
        jsonViewerBtn.setAttribute('role', 'button');
        jsonViewerBtn.setAttribute('aria-label', 'View Message JSON');
        jsonViewerBtn.setAttribute('tabindex', '0');
        jsonViewerBtn.title = 'View Message JSON';
        jsonViewerBtn.style.cssText = `
            position: relative;
            width: auto;
            height: 24px;
            margin: 0 8px;
            cursor: pointer;
            color: var(--interactive-normal);
            flex: 0 0 auto;
            display: flex;
            align-items: center;
            justify-content: center;
        `;
        jsonViewerBtn.innerHTML = jsonSvgIcon;
        
        // Add click handler
        jsonViewerBtn.onclick = () => {
            if (jsonDisplayBox) {
                jsonDisplayBox.style.display = jsonDisplayBox.style.display === 'none' ? 'flex' : 'none';
            } else {
                createJsonDisplayBox();
                jsonDisplayBox.style.display = 'flex';
            }
        };
        
        // Add hover effects
        jsonViewerBtn.onmouseover = () => {
            jsonViewerBtn.style.color = 'var(--interactive-hover)';
        };
        
        jsonViewerBtn.onmouseout = () => {
            jsonViewerBtn.style.color = 'var(--interactive-normal)';
        };
        
        return jsonViewerBtn;
    }
    
    function mountJsonViewerButton() {
        const toolbar = document.querySelector('#app-mount [class^=toolbar]');
        if (toolbar) {
            // Check if button already exists
            if (!document.getElementById('json-viewer-btn')) {
                if (!jsonViewerBtn) {
                    jsonViewerBtn = createJsonViewerButton();
                }
                toolbar.appendChild(jsonViewerBtn);
                console.log('Mounted JSON Viewer button');
            }
        }
    }
    
    function initJsonViewer() {
        // Insert CSS
        insertCss(themeCss);
        
        // Create button
        createJsonViewerButton();
        
        // Create display box (initially hidden)
        createJsonDisplayBox();
        
        // Mount button
        mountJsonViewerButton();
        
        // Add message click listener
        document.addEventListener('click', handleMessageClick, true);
        
        // Setup observer to re-mount button if needed
        const discordElm = document.querySelector('#app-mount');
        if (discordElm) {
            const observer = new MutationObserver(() => {
                if (observerThrottle) return;
                observerThrottle = setTimeout(() => {
                    observerThrottle = null;
                    if (!document.body.contains(jsonViewerBtn)) {
                        mountJsonViewerButton();
                    }
                }, 3000);
            });
            
            observer.observe(discordElm, { childList: true, subtree: true });
        }
        
        console.log('JSON Viewer initialized successfully');
    }

    // Initialize when the page loads
    if (document.readyState === 'loading') {
        window.addEventListener('DOMContentLoaded', initJsonViewer);
    } else {
        initJsonViewer();
    }
})();