import Emotes from './Emotes'; import Message from './Message'; import PersistentSyncStorage from 'src/helpers/PersistentSyncStorage'; class ChatWatcher { constructor() { this.watchChat = this.watchChat.bind(this); this._chatContainer = null; this._observer = null; this.messages = new Map(); } init() { return new Promise((res, rej) => { this.getChatContainer().then(Emotes.init).then(() => { this.addEmotePopup(); this.watchChat(); this.parsePreloadedMessages(); }); }); } getChatContainer() { // Parent of actual chat (children are messages) const checkForContainer = (res, rej) => { this._chatContainer = document.querySelector('#items.style-scope.yt-live-chat-item-list-renderer'); if(this._chatContainer !== null) { res(); } else { setTimeout(checkForContainer.bind(this, res, rej), 250); } }; return new Promise(checkForContainer); } parsePreloadedMessages() { const messages = this._chatContainer.children; for(let i = messages.length-1; i >= 0; i--) { const node = messages[i]; if(this.isMessageNode(node)) { const message = new Message(node); } } } watchChat() { console.log('Chat observer started'); this._observer = new MutationObserver(mutations => { mutations.forEach(mutation => { const { addedNodes, removedNodes } = mutation; // Added nodes if(typeof addedNodes !== 'undefined' && addedNodes.length > 0) { for(let i = 0, length = addedNodes.length-1; i <= length; i++) { const node = addedNodes[i]; if(this.isMessageNode(node)) { this.onNewMessage(node); } } } // Removed nodes if(typeof removedNodes !== 'undefined' && removedNodes.length > 0) { for(let i = 0, length = removedNodes.length-1; i <= length; i++) { const node = removedNodes[i]; if(this.isMessageNode(node) && this.isObservedMessage(node)) { this.onObservedMessageRemoved(node); } } } }); }); this._observer.observe(this._chatContainer, { childList: true, attributes: false, characterData: false, subtree: false }); } onNewMessage(node) { const message = new Message(node); // Don't store message if has 0 emotes if(message.hasEmotes) { this.messages.set(message.id, message); } } onObservedMessageRemoved(node) { const messageId = node.getAttribute('message-id'); const message = this.messages.get(messageId); if(message != undefined){ message.destroy(); } this.messages.delete(messageId); } isMessageNode(node) { return node.tagName === 'YT-LIVE-CHAT-TEXT-MESSAGE-RENDERER'; } isObservedMessage(node) { return node.getAttribute('message-id') !== null; } /////////////////////////////////////////////////////////////////// addEmotePopup(){ // create emote button const emoteButton = document.createElement('button'); emoteButton.classList.add('emoteButton'); emoteButton.textContent = ''; // append button to action-buttons list const chatButtonSelectionList = document.getElementById('action-buttons'); chatButtonSelectionList.parentNode.insertBefore(emoteButton, chatButtonSelectionList); // create popupDiv const popUpDiv = document.createElement('div'); popUpDiv.classList.add('popup'); popUpDiv.classList.add('hideElement'); function emoteAppend(keysITer){ // create divider var hr = document.createElement('hr'); hr.classList.add('emoteDivider'); for (let index = 0; index < keysITer.length; index++) { const element = keysITer[index]; var emote_div = document.createElement('emote_div'); emote_div.innerHTML = (Emotes.get(element).html); popUpDiv.appendChild(emote_div); } popUpDiv.appendChild(hr); } // create text var bttv_text = document.createElement('h2'); bttv_text.textContent = "BTTV"; bttv_text.classList.add('emotePopUpText'); var franker_text = document.createElement('h2'); franker_text.textContent = "FrankerFacez"; franker_text.classList.add('emotePopUpText'); var twitch_text = document.createElement('h2'); twitch_text.textContent = "Twitch"; twitch_text.classList.add('emotePopUpText'); // need ittr to search each dict and append to dom let keysITer = null; keysITer = Array.from(Emotes.specialEmotesDictionary.keys()); emoteAppend(keysITer); if(PersistentSyncStorage.data.options.enableBTTVEmotes){ popUpDiv.appendChild(bttv_text); keysITer = Array.from(Emotes.bttv_Dictionary.keys()); emoteAppend(keysITer); } if(PersistentSyncStorage.data.options.enableFrankerEmotes){ popUpDiv.appendChild(franker_text); keysITer = Array.from(Emotes.franker_Dictionary.keys()); emoteAppend(keysITer); } if(PersistentSyncStorage.data.options.enableTwitchEmotes){ popUpDiv.appendChild(twitch_text); keysITer = Array.from(Emotes.twitch_Dictionary.keys()); emoteAppend(keysITer); } // add div to doc chatButtonSelectionList.appendChild(popUpDiv); // listen for popup button emoteButton.addEventListener('click', function(){ popUpDiv.classList.toggle('hideElement'); console.log('emote popup button clicked'); }); // get input area var inputArea = document.querySelector('#input.yt-live-chat-text-input-field-renderer'); var inputAreaLabel = document.querySelector('#label.yt-live-chat-text-input-field-renderer'); // add alt tag to chat function emoteToTextArea(){ inputArea.textContent += this.alt + " "; inputArea.focus(); inputAreaLabel.textContent = ""; popUpDiv.classList.toggle('hideElement'); console.log(this.alt + " emote button selected"); } // listener button for emotes var EMOTICONS = document.getElementsByTagName('img'); for (let index = 0; index < EMOTICONS.length; index++) { const element = EMOTICONS[index]; element.addEventListener('click', emoteToTextArea, false); } console.log((keysITer.length+1) + " Emotes Added"); }// end addEmotePopup /////////////////////////////////////////////////////////////////// }// end chat watcher export default ChatWatcher;