Soop(숲) 채팅 확장 스크립트

채팅창 편의기능 추가

// ==UserScript==
// @name         Soop(숲) 채팅 확장 스크립트
// @namespace    https://greasyfork.org/scripts/512780
// @icon         https://res.sooplive.co.kr/afreeca.ico
// @version      1.2.6
// @description  채팅창 편의기능 추가
// @match        https://play.sooplive.co.kr/*
// @match        https://vod.sooplive.co.kr/*
// @license      MIT
// @author       ekzmchoco
// @grant        none
// ==/UserScript==
// Referenced Code: https://greasyfork.org/scripts/512724

(function() {
    'use strict';

    const DEFAULT_SETTINGS = {
        chatWidthAdjustment: true,
        customEmoticonBox: true,
        allowPasteInChat: true,
        emoticonButtonReposition: true,
        emoticonButtonColor: false,
        emoticonWindowPositionChange: true,
        autoSendAfterEmoticons: false
    };

    const userSettings = JSON.parse(localStorage.getItem('soopChatSettings')) || DEFAULT_SETTINGS;

    function saveSettings() {
        localStorage.setItem('soopChatSettings', JSON.stringify(userSettings));
    }

    function initSettingsUI() {
        const chattingArea = document.querySelector("#chatting_area");
        if (!chattingArea) return;

        const personSettingEl = chattingArea.querySelector(".chat_layer.sub.person .contents > ul");
        if (!personSettingEl) return;

        if (document.getElementById('script-settings')) return;

        const settingsLI = document.createElement("li");
        settingsLI.id = 'script-settings';

        const settingsOptions = [
            { key: 'chatWidthAdjustment', label: '채팅 너비 조절 기능' },
            { key: 'customEmoticonBox', label: '커스텀 이모티콘 박스*' },
            { key: 'allowPasteInChat', label: '채팅 붙여넣기 허용*' },
            { key: 'emoticonButtonReposition', label: '이모티콘 버튼 위치 변경*' },
            { key: 'emoticonButtonColor', label: '이모티콘 버튼 색상 (밝게/어둡게)*' },
            { key: 'emoticonWindowPositionChange', label: '이모티콘 창 위치 변경*' },
            { key: 'autoSendAfterEmoticons', label: '이모티콘 입력 후 자동 전송' }
        ];

        settingsOptions.forEach(option => {
            const div = document.createElement("div");
            div.classList.add("checkbox_wrap");

            const checkbox = document.createElement("input");
            checkbox.type = "checkbox";
            checkbox.id = option.key;
            checkbox.checked = userSettings[option.key];

            const label = document.createElement("label");
            label.htmlFor = option.key;
            label.textContent = option.label;

            checkbox.addEventListener("change", () => {
                userSettings[option.key] = checkbox.checked;
                saveSettings();
                applySettings(option.key);

                if (option.label.includes('*')) {
                    alert('이 설정은 페이지를 새로고침해야 적용됩니다.');
                }
            });

            div.appendChild(checkbox);
            div.appendChild(label);
            settingsLI.appendChild(div);
        });

        personSettingEl.appendChild(settingsLI);
    }

    function applySettings(optionKey) {
        switch(optionKey) {
            case 'chatWidthAdjustment':
                if (userSettings.chatWidthAdjustment) {
                    initChatWidthAdjustment();
                } else {
                    removeChatWidthAdjustment();
                }
                break;
            case 'customEmoticonBox':
                if (userSettings.customEmoticonBox) {
                    initCustomEmoticonBox();
                } else {
                    removeCustomEmoticonBox();
                }
                break;
            case 'allowPasteInChat':
                if (userSettings.allowPasteInChat) {
                    enablePasteInChat();
                } else {
                    alert('이 설정은 페이지를 새로고침해야 적용됩니다.');
                }
                break;
            case 'emoticonButtonColor':
                if (userSettings.emoticonButtonReposition) {
                    alert('이 설정은 페이지를 새로고침해야 적용됩니다.');
                }
                break;
            case 'autoSendAfterEmoticons':
            case 'emoticonButtonReposition':
            case 'emoticonWindowPositionChange':
                break;
            default:
                break;
        }
    }

    function init() {
        initSettingsUI();

        if (userSettings.allowPasteInChat) {
            enablePasteInChat();
        }

        if (userSettings.chatWidthAdjustment) {
            initChatWidthAdjustment();
        }

        if (userSettings.customEmoticonBox) {
            initCustomEmoticonBox();
        }

        if (userSettings.emoticonButtonReposition || userSettings.emoticonWindowPositionChange) {
            initEmoticonRelatedFeatures();
        }
    }

    function initChatWidthAdjustment() {
        const chattingArea = document.querySelector("#chatting_area");
        if (!chattingArea) return;

        const chatTitleDiv = chattingArea.querySelector(".chat_title");
        if (!chatTitleDiv) return;

        if (document.getElementById('chatWidthSlider')) return;

        let ul = chatTitleDiv.querySelector("ul");
        if (!ul) {
            ul = document.createElement("ul");
            chatTitleDiv.appendChild(ul);
        }

        let insertBeforeElement = ul.querySelector("#setbox_viewer") || ul.querySelector("#setbox_set");
        if (!insertBeforeElement) {
            insertBeforeElement = ul.querySelector("li.set");
        }
        if (!insertBeforeElement) {
            insertBeforeElement = ul.firstChild;
        }

        const sliderLi = document.createElement("li");
        sliderLi.style.padding = "0 10px";
        sliderLi.style.display = "flex";
        sliderLi.style.alignItems = "center";

        const rangeInput = document.createElement("input");
        rangeInput.type = "range";
        rangeInput.min = 300;
        rangeInput.max = 450;
        rangeInput.step = 5;
        rangeInput.value = localStorage.getItem("customChattingAreaWidth")
          ? localStorage.getItem("customChattingAreaWidth")
          : 380;
        rangeInput.style.width = "80px";
        rangeInput.style.marginRight = "1px";
        rangeInput.id = 'chatWidthSlider';

        const rangeLabel = document.createElement("span");
        rangeLabel.style.color = "#fff";
        rangeLabel.style.fontSize = "12px";

        rangeInput.addEventListener("input", () => {
            changeChatAreaWidth(rangeInput.value);
            localStorage.setItem("customChattingAreaWidth", rangeInput.value);
        });

        sliderLi.appendChild(rangeInput);
        sliderLi.appendChild(rangeLabel);

        if (insertBeforeElement && insertBeforeElement.nextSibling) {
            ul.insertBefore(sliderLi, insertBeforeElement.nextSibling);
        } else {
            ul.appendChild(sliderLi);
        }

        const chatStyleEl = document.createElement("style");
        chatStyleEl.id = 'custom-chat-width-style';
        document.head.append(chatStyleEl);

        function changeChatAreaWidth(width) {
            chatStyleEl.textContent = `
                #webplayer.chat_open {
                    --chatting_W: ${width}px;
                }
                #webplayer.chat_open .chatting_area {
                    width: var(--chatting_W);
                }

                .vod #chatting_area {
                    --chatting_W: ${width}px;
                    width: var(--chatting_W);
                    position: fixed;
                    right: 0;
                    left: auto;
                }
            `;
        }

        const storedWidth = localStorage.getItem("customChattingAreaWidth") || 380;
        changeChatAreaWidth(storedWidth);
        rangeInput.value = storedWidth;
    }

    function removeChatWidthAdjustment() {
        const chatWidthSlider = document.getElementById('chatWidthSlider');
        if (chatWidthSlider) {
            chatWidthSlider.parentElement.remove();
        }
        const chatStyleEl = document.getElementById('custom-chat-width-style');
        if (chatStyleEl) {
            chatStyleEl.remove();
        }

        const chattingArea = document.querySelector("#chatting_area");
        if (chattingArea) {
            chattingArea.style.width = '';
            chattingArea.style.position = '';
            chattingArea.style.right = '';
            chattingArea.style.left = '';
        }
    }

    function initCustomEmoticonBox() {
        const chattingArea = document.querySelector("#chatting_area");
        const actionBox = chattingArea.querySelector("#actionbox");
        if (!actionBox) return;

        if (document.querySelector(".customEmojiBtn")) return;

        const emoticonBox = document.querySelector("#emoticonBox");
        if (!emoticonBox) return;

        const recentEmoticonBtn = emoticonBox.querySelector(
            ".tab_area .item_list ul > li[data-type='RECENT'] .ic_clock"
        );
        const subTabArea = emoticonBox.querySelector(".subTab_area");
        const defaultSubTab = subTabArea.querySelector("li[data-type='DEFAULT']");
        const OGQSubTab = subTabArea.querySelector("li[data-type='OGQ']");

        if (!recentEmoticonBtn || !defaultSubTab || !OGQSubTab) return;

        function defaultEmoticonClick() {
            recentEmoticonBtn.click();
            setTimeout(() => {
                defaultSubTab.click();
            }, 100);
        }
        function OGQEmoticonClick() {
            recentEmoticonBtn.click();
            setTimeout(() => {
                OGQSubTab.click();
            }, 100);
        }

        const chattingItemWrap = chattingArea.querySelector(".chatting-item-wrap");
        const chatArea = chattingItemWrap.querySelector("#chat_area");
        const customEmojiBox = document.createElement("div");
        customEmojiBox.classList.add("customEmojiBox");
        let isLoading = false;

        const sliderContainer = document.createElement('div');
        sliderContainer.classList.add('slider-container');
        sliderContainer.style.display = 'none';

        const sliderWrapper = document.createElement('div');
        sliderWrapper.classList.add('slider-wrapper');

        const sliderTrack = document.createElement('div');
        sliderTrack.classList.add('slider-track');

        const sliderRange = document.createElement('div');
        sliderRange.classList.add('slider-range');

        const minSlider = document.createElement('input');
        minSlider.type = 'range';
        minSlider.min = '1';
        minSlider.max = '10';
        minSlider.value = parseInt(localStorage.getItem('minSliderValue')) || 3;
        minSlider.classList.add('slider');
        minSlider.id = 'slider-min';

        const maxSlider = document.createElement('input');
        maxSlider.type = 'range';
        maxSlider.min = '1';
        maxSlider.max = '10';
        maxSlider.value = parseInt(localStorage.getItem('maxSliderValue')) || 5;
        maxSlider.classList.add('slider');
        maxSlider.id = 'slider-max';

        const rangeDisplay = document.createElement('div');
        rangeDisplay.classList.add('range-display');
        rangeDisplay.textContent = `${minSlider.value}-${maxSlider.value}`;

        sliderWrapper.appendChild(sliderTrack);
        sliderWrapper.appendChild(sliderRange);
        sliderWrapper.appendChild(minSlider);
        sliderWrapper.appendChild(maxSlider);

        sliderContainer.appendChild(sliderWrapper);
        sliderContainer.appendChild(rangeDisplay);

        function updateRange() {
            const min = parseInt(minSlider.value);
            const max = parseInt(maxSlider.value);

            if (min > max) {
                if (this === minSlider) {
                    maxSlider.value = min;
                } else {
                    minSlider.value = max;
                }
            }

            const minVal = parseInt(minSlider.value);
            const maxVal = parseInt(maxSlider.value);

            const leftPercent = ((minVal - 1) / 9) * 100;
            const rightPercent = ((maxVal - 1) / 9) * 100;
            sliderRange.style.left = leftPercent + '%';
            sliderRange.style.right = (100 - rightPercent) + '%';

            rangeDisplay.textContent = `${minVal}-${maxVal}`;

            localStorage.setItem('minSliderValue', minVal);
            localStorage.setItem('maxSliderValue', maxVal);
        }

        minSlider.addEventListener('input', updateRange);
        maxSlider.addEventListener('input', updateRange);

        updateRange();

        function renderEmoticon(type = "default") {
            if (isLoading) return;
            isLoading = true;

            type === "default" ? defaultEmoticonClick() : OGQEmoticonClick();

            setTimeout(() => {
                proceedWithRender(type);
                isLoading = false;
            }, 500);
        }

        function proceedWithRender(type) {
            const diffType = type === "default" ? "OGQ" : "default";
            const isOn = customEmojiBox.classList.contains(type);
            const isDiffOn = customEmojiBox.classList.contains(diffType);

            if (isOn) {
                customEmojiBox.classList.remove(type);
                customEmojiBox.innerHTML = "";
                customEmojiBox.style.display = "none";
                chatArea.style.bottom = "0";
                sliderContainer.style.display = "none";
                return;
            }

            if (isDiffOn) {
                customEmojiBox.classList.remove(diffType);
                customEmojiBox.innerHTML = "";
            }

            const emoticonItemBox = emoticonBox.querySelector(".emoticon_item");
            if (!emoticonItemBox) {
                console.error("이모티콘 아이템을 찾을 수 없습니다.");
                return;
            }

            const itemList = [];
            emoticonItemBox.querySelectorAll("span > a")?.forEach((item, index) => {
                if (index < 21) {
                    const itemClone = item.cloneNode(true);

                    itemClone.addEventListener("click", () => {
                        const minCount = parseInt(minSlider.value);
                        const maxCount = parseInt(maxSlider.value);
                        const repeatCount = Math.floor(Math.random() * (maxCount - minCount + 1)) + minCount;

                        for (let i = 0; i < repeatCount; i++) {
                            item.click();
                        }
                        if (userSettings.autoSendAfterEmoticons) {
                            setTimeout(() => {
                                const sendBtn = document.querySelector("#btn_send");
                                if (sendBtn) sendBtn.click();
                            }, 100);
                        }
                    });

                    itemList.push(itemClone);
                }
            });

            customEmojiBox.innerHTML = '';
            customEmojiBox.append(...itemList);

            if (!chattingItemWrap.contains(customEmojiBox)) {
                chattingItemWrap.append(customEmojiBox);
            }
            customEmojiBox.style.display = "flex";
            customEmojiBox.classList.add(type);
            chatArea.style.position = "relative";
            chatArea.style.bottom = `${customEmojiBox.offsetHeight + sliderContainer.offsetHeight + 8}px`;

            if (!chattingItemWrap.contains(sliderContainer)) {
                chattingItemWrap.append(sliderContainer);
            }
            sliderContainer.style.display = 'flex';
        }

        const recentEmoticonCustomBtnLI = document.createElement("li");
        const recentEmoticonCustomBtn = document.createElement("a");
        recentEmoticonCustomBtn.href = "javascript:;";
        recentEmoticonCustomBtn.classList.add("customEmojiBtn");
        recentEmoticonCustomBtn.textContent = "최근";
        recentEmoticonCustomBtnLI.append(recentEmoticonCustomBtn);

        const OGQEmoticonCustomBtnLI = document.createElement("li");
        const OGQEmoticonCustomBtn = document.createElement("a");
        OGQEmoticonCustomBtn.href = "javascript:;";
        OGQEmoticonCustomBtn.classList.add("customEmojiBtn");
        OGQEmoticonCustomBtn.textContent = "OGQ";
        OGQEmoticonCustomBtnLI.append(OGQEmoticonCustomBtn);

        recentEmoticonCustomBtnLI.addEventListener("click", () => {
            renderEmoticon("default");
        });
        OGQEmoticonCustomBtnLI.addEventListener("click", () => {
            renderEmoticon("OGQ");
        });

        const itemBox = actionBox.querySelector(".item_box");
        if (itemBox) {
            itemBox.append(recentEmoticonCustomBtnLI, OGQEmoticonCustomBtnLI);
        }

        const iconColor = userSettings.emoticonButtonColor ? '#333' : '#D5D7DC';
        const rangeTextColor = userSettings.emoticonButtonColor ? '#000' : '#fff';

        const defaultStyleEl = document.createElement("style");
        const defaultStyle = `
        .chatbox .actionbox .chat_item_list .item_box li a.customEmojiBtn {
          line-height: 32px;
          font-size: 15px;
          font-weight: bold;
          color: ${iconColor};
          background-color: transparent;
        }
        .chatbox .actionbox .chat_item_list .item_box li a.customEmojiBtn:hover {
          color: ${iconColor};
          background-color: transparent;
        }
        .chatting-item-wrap .customEmojiBox {
          position: absolute;
          margin-bottom: 20px;
          bottom: 0;
          left: 0;
          width: 100%;
          display: flex;
          flex-wrap: wrap;
          gap: 8px 4px;
          padding: 8px 8px 0 8px;
          background-color: #fefefe;
        }
        [dark="true"] .chatting-item-wrap .customEmojiBox {
          background-color: #222;
          border-top: 1px solid #444;
        }
        .chatting-item-wrap .customEmojiBox a {
          width: 36px;
          height: 36px;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          border-radius: 4px;
        }
        .chatting-item-wrap .customEmojiBox a:hover {
          background-color: rgba(117, 123, 138, 0.2);
        }
        .slider-container {
          position: absolute;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 20px;
          margin: 0;
          background-color: #fefefe;
          display: flex;
          align-items: center;
          padding: 0 8px 8px 8px;
          flex-wrap: nowrap;
        }
        [dark="true"] .slider-container {
          background-color: #222;
        }
        .slider-wrapper {
            position: relative;
          width: 90%;
          height: 20px;
        }
        .range-display {
          width: 10%;
          text-align: center;
          font-size: 12px;
          color: ${rangeTextColor};
          margin-left: 8px;
        }
        .slider-track {
          position: absolute;
          top: 50%;
          left: 0;
          right: 0;
          height: 3px;
          background: #ddd;
          border-radius: 10px;
          transform: translateY(-50%);
        }
        .slider {
          position: absolute;
          top: 50%;
          left: 0;
          width: 100%;
          -webkit-appearance: none;
          appearance: none;
          background: none;
          pointer-events: none;
          transform: translateY(-50%);
        }
        .slider-range {
          position: absolute;
          top: 50%;
          height: 3px;
          background: #4444ff;
          border-radius: 10px;
          pointer-events: none;
          transform: translateY(-50%);
        }
        .slider::-webkit-slider-thumb {
          -webkit-appearance: none;
          appearance: none;
          width: 15px;
          height: 15px;
          border-radius: 50%;
          background: #ffffff;
          border: 2px solid #4444ff;
          cursor: pointer;
          pointer-events: all;
          box-shadow: 0 0 5px rgba(0,0,0,0.2);
          position: relative;
          z-index: 1;
        }
        .slider::-moz-range-thumb {
          width: 15px;
          height: 15px;
          border-radius: 50%;
          background: #ffffff;
          border: 2px solid #4444ff;
          cursor: pointer;
          pointer-events: all;
          box-shadow: 0 0 5px rgba(0,0,0,0.2);
          position: relative;
          z-index: 1;
        }
        `;
        defaultStyleEl.textContent = defaultStyle;
        document.head.append(defaultStyleEl);
    }

    function removeCustomEmoticonBox() {
        const customEmojiBtns = document.querySelectorAll('.customEmojiBtn');
        customEmojiBtns.forEach(btn => btn.parentElement.remove());
        const customEmojiBox = document.querySelector('.customEmojiBox');
        if (customEmojiBox) customEmojiBox.remove();

        const sliderContainer = document.querySelector('.slider-container');
        if (sliderContainer) sliderContainer.remove();

        const styleEl = document.querySelector('#custom-chat-width-style');
        if (styleEl) {
            styleEl.remove();
        }

        const styleCustomEl = document.querySelector('style');
        if (styleCustomEl && styleCustomEl.textContent.includes('.customEmojiBox')) {
            styleCustomEl.remove();
        }
    }

    function enablePasteInChat() {
        const writeArea = document.querySelector("#write_area");
        if (!writeArea) return;

        writeArea.removeAttribute("readonly");
        writeArea.removeAttribute("disabled");

        writeArea.addEventListener("paste", function(e) {
            e.preventDefault();
            const clipboardData = (e.originalEvent || e).clipboardData || window.clipboardData;
            const pastedData = clipboardData.getData('text');
            document.execCommand('insertText', false, pastedData);
        });
    }

    function initEmoticonRelatedFeatures() {
        const observer = new MutationObserver((mutations, obs) => {
            const ul = document.querySelector('ul.item_box');
            if (!ul) return;

            const btnStarLi = document.getElementById('btn_star');
            const btnAdballoonLi = document.getElementById('btn_adballoon');
            const sooptoreLi = ul.querySelector('li.sooptore');
            const btnEmo = document.getElementById('btn_emo');

            if (!btnStarLi || !btnAdballoonLi || !sooptoreLi || !btnEmo) return;

            btnStarLi.classList.remove('off');
            btnAdballoonLi.classList.remove('off');
            sooptoreLi.classList.remove('off');
            btnEmo.classList.remove('off');

            if (userSettings.emoticonButtonReposition) {
                const chatWriteDiv = document.getElementById('chat_write');
                if (chatWriteDiv && chatWriteDiv.contains(btnEmo)) {
                    chatWriteDiv.removeChild(btnEmo);
                }

                let btnEmoLi = document.createElement('li');
                btnEmoLi.id = 'btn_emo_li';
                btnEmoLi.className = 'emoticon';

                btnEmoLi.appendChild(btnEmo);

                if (ul.firstChild !== btnEmoLi) {
                    ul.insertBefore(btnEmoLi, ul.firstChild);
                }

                ul.appendChild(btnStarLi);
                ul.appendChild(btnAdballoonLi);
                ul.appendChild(sooptoreLi);

                btnStarLi.classList.add('right-align');

                const iconColor = userSettings.emoticonButtonColor ? '#333' : '#D5D7DC';

                const svgIcon = encodeURIComponent(
                    `<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' fill='none'>
                        <g opacity='1'>
                            <path fill='${iconColor}' d='M19.56 18.396a.498.498 0 1 1 .86.506c-.598 1.015-1.973 2.735-4.421 2.735-2.445 0-3.82-1.717-4.418-2.73a.497.497 0 0 1 .176-.684.5.5 0 0 1 .684.176c.498.845 1.617 2.24 3.558 2.24 1.943 0 3.063-1.397 3.56-2.243Z'/>
                            <path stroke='${iconColor}' stroke-width='.4' d='M11.581 18.906c.598 1.014 1.973 2.732 4.418 2.732 2.448 0 3.823-1.72 4.42-2.736a.498.498 0 1 0-.86-.506c-.497.846-1.617 2.243-3.56 2.243-1.94 0-3.06-1.395-3.559-2.24a.5.5 0 0 0-.683-.176.497.497 0 0 0-.176.683Zm0 0 .078-.045'/>
                            <path fill='${iconColor}' stroke='${iconColor}' stroke-width='.45' d='M19.527 15.805a1.227 1.227 0 1 1 0-2.455 1.227 1.227 0 0 1 0 2.455ZM12.477 15.805a1.228 1.228 0 1 1 .001-2.456 1.228 1.228 0 0 1 0 2.456Z'/>
                            <path stroke='${iconColor}' stroke-width='1.4' d='M16 25.8a9.3 9.3 0 1 1 0-18.6 9.3 9.3 0 0 1 0 18.6Z'/>
                        </g>
                    </svg>`
                );

                const dataURL = `data:image/svg+xml,${svgIcon}`;

                btnEmo.style.backgroundImage = `url("${dataURL}")`;
                btnEmo.style.backgroundRepeat = 'no-repeat';
                btnEmo.style.backgroundPosition = 'center';
                btnEmo.style.backgroundSize = 'contain';
                btnEmo.style.width = '32px';
                btnEmo.style.height = '32px';
                btnEmo.style.border = 'none';
                btnEmo.style.cursor = 'pointer';
                btnEmo.style.padding = '0';
                btnEmo.style.margin = '0';
                btnEmo.style.backgroundColor = 'transparent';
                btnEmo.textContent = '';
            }

            if (userSettings.emoticonWindowPositionChange) {
                const emoticonContainer = document.getElementById('emoticonContainer');
                if (emoticonContainer) {
                    const styleEl = document.createElement('style');
                    styleEl.id = 'custom-emoticon-position-style';
                    styleEl.textContent = `
                    .chatbox #emoticonContainer {
                        bottom: 10px;
                        transform: translateX(0);
                        transition: none !important;
                    }
                    .chatbox #emoticonContainer.on {
                        bottom: 10px;
                        max-width: 360px;
                        min-width: 320px;
                        right: unset;
                        left: 0;
                        transform: translateX(-105%);
                        transition: none !important;
                    }
                    `;
                    document.head.appendChild(styleEl);
                }
            }

            if (!document.getElementById('sooplive-custom-style')) {
                const style = document.createElement('style');
                style.id = 'sooplive-custom-style';
                style.innerHTML = `
                    ul.item_box {
                        display: flex;
                        align-items: center;
                    }
                    ul.item_box li {
                        margin: 0 3px;
                    }
                    ul.item_box li.right-align {
                        margin-left: auto;
                    }
                `;
                document.head.appendChild(style);
            }

            obs.disconnect();
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    function startScript() {
        if (document.querySelector("#chatting_area")) {
            init();
        } else {
            setTimeout(startScript, 500);
        }
    }

    startScript();

})();