adding dev files to master
adding all the dev files, package, webpack etc, including build which will contain minified release
This commit is contained in:
48
src/content/ChatScroller.js
Normal file
48
src/content/ChatScroller.js
Normal file
@@ -0,0 +1,48 @@
|
||||
class ChatScroller {
|
||||
constructor() {
|
||||
this.scroll = this.scroll.bind(this);
|
||||
this.start = this.start.bind(this);
|
||||
this.stop = this.stop.bind(this);
|
||||
|
||||
this.scroller = null;
|
||||
this.interval = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.getScroller()
|
||||
.then(() => {
|
||||
//this.scroller.addEventListener('mouseleave', this.start);
|
||||
this.scroller.addEventListener('mouseenter', this.stop);
|
||||
this.start();
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
this.interval = setInterval(
|
||||
this.scroll,
|
||||
250
|
||||
);
|
||||
}
|
||||
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
scroll() {
|
||||
this.scroller.scrollTop = 9999;
|
||||
}
|
||||
|
||||
getScroller() {
|
||||
const checkForScroller = (res, rej) => {
|
||||
this.scroller = document.getElementById('item-scroller');
|
||||
if(this.scroller !== null) {
|
||||
res();
|
||||
} else {
|
||||
setTimeout(checkForScroller.bind(this, res, rej), 250);
|
||||
}
|
||||
};
|
||||
return new Promise(checkForScroller);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatScroller;
|
||||
230
src/content/ChatWatcher.js
Normal file
230
src/content/ChatWatcher.js
Normal file
@@ -0,0 +1,230 @@
|
||||
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;
|
||||
16
src/content/Emotes/Emote.js
Normal file
16
src/content/Emotes/Emote.js
Normal file
@@ -0,0 +1,16 @@
|
||||
class Emote {
|
||||
constructor({ code, url }) {
|
||||
this.code = code;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
get html() {
|
||||
return (`
|
||||
<span class="Emote">
|
||||
<img title="${this.code}" src="${this.url}" alt="${this.code}">
|
||||
</span>
|
||||
`).trim();
|
||||
}
|
||||
}
|
||||
|
||||
export default Emote;
|
||||
194
src/content/Emotes/index.js
Normal file
194
src/content/Emotes/index.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import PersistentSyncStorage from 'src/helpers/PersistentSyncStorage';
|
||||
import Emote from './Emote';
|
||||
|
||||
|
||||
class Emotes {
|
||||
constructor() {
|
||||
this.dictionary = new Map();
|
||||
|
||||
// identification for popup
|
||||
this.twitch_Dictionary = new Map();
|
||||
this.bttv_Dictionary = new Map();
|
||||
this.franker_Dictionary = new Map();
|
||||
this.specialEmotesDictionary = new Map();
|
||||
|
||||
|
||||
this.init = this.init.bind(this);
|
||||
}
|
||||
|
||||
init() {
|
||||
return Promise.all([
|
||||
(PersistentSyncStorage.data.options.enableBTTVEmotes && this.loadBTTVEmote()),
|
||||
(PersistentSyncStorage.data.options.enableFrankerEmotes && this.loadFrankerEmotes()),
|
||||
(PersistentSyncStorage.data.options.enableTwitchEmotes && this.loadTwitchEmotes()),
|
||||
(this.specialEmotes())
|
||||
]);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
get(key) {
|
||||
return this.dictionary.get(key);
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
return this.dictionary.set(key, new Emote(value));
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.dictionary.has(key);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
bbtv_ToDict(json){
|
||||
for (let index = 0; index < json.length; index++) {
|
||||
|
||||
const { emote, total } = json[index];
|
||||
|
||||
const url = `https://cdn.betterttv.net/emote/${emote.id}/3x`;
|
||||
|
||||
this.dictionary.set(emote.code, new Emote({ code: emote.code, url }));
|
||||
this.bttv_Dictionary.set(emote.code, new Emote({ code: emote.code, url }));
|
||||
}
|
||||
}
|
||||
|
||||
bbtv_cached_ToDict(json){
|
||||
for (let index = 0; index < json.length; index++) {
|
||||
|
||||
const { id, code } = json[index];
|
||||
|
||||
const url = `https://cdn.betterttv.net/emote/${id}/3x`;
|
||||
|
||||
this.dictionary.set(code, new Emote({ code: code, url }));
|
||||
this.bttv_Dictionary.set(code, new Emote({ code: code, url }));
|
||||
}
|
||||
}
|
||||
|
||||
// loadEmote is where we collect an object array of emotes from bttv api
|
||||
async loadBTTVEmote(){
|
||||
|
||||
// top 100 emotes query = ?limit=100&offset=100
|
||||
const bttv_top_api_url = "https://api.betterttv.net/3/emotes/shared/top?limit=100";
|
||||
const bttv_top_api_response = await fetch(bttv_top_api_url);
|
||||
var top_Json = await bttv_top_api_response.json();
|
||||
|
||||
// tredning emotes
|
||||
const bttv_trending_api_url = "https://api.betterttv.net/3/emotes/shared/trending?limit=100";
|
||||
const bttv_trending_api_response = await fetch(bttv_trending_api_url);
|
||||
var trending_Json = await bttv_trending_api_response.json();
|
||||
|
||||
// global emotes are weird, stored in seperate cache and do not give all the normal attributes
|
||||
const bttv_global_api_url = "https://api.betterttv.net/3/cached/emotes/global";
|
||||
const bttv_global_api_response = await fetch(bttv_global_api_url);
|
||||
var global_Json = await bttv_global_api_response.json();
|
||||
|
||||
this.bbtv_ToDict(top_Json);
|
||||
this.bbtv_ToDict(trending_Json);
|
||||
this.bbtv_cached_ToDict(global_Json);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
frankerToDict(json){
|
||||
for (let index = 0; index < json.emoticons.length; index++) {
|
||||
|
||||
const { name, urls } = json.emoticons[index];
|
||||
|
||||
var url = "";
|
||||
if(urls[4] != undefined){
|
||||
url = urls[4];
|
||||
}else if(urls[2] != undefined){
|
||||
url = urls[2];
|
||||
}else{
|
||||
url = urls[1];
|
||||
}
|
||||
|
||||
this.dictionary.set(name, new Emote({ code: name, url }));
|
||||
this.franker_Dictionary.set(name, new Emote({ code: name, url }));
|
||||
}
|
||||
}
|
||||
|
||||
// loadFrankerEmotes is where we collect an object array of emotes from franker api
|
||||
async loadFrankerEmotes(){
|
||||
|
||||
const franker_top_api_url = "https://api.frankerfacez.com/v1/emoticons?sort=count-desc";
|
||||
|
||||
const first50Response = await fetch(franker_top_api_url);
|
||||
var first50json = await first50Response.json();
|
||||
var next50Link = first50json._links.next;
|
||||
const second50Response = await fetch(next50Link);
|
||||
var second50json = await second50Response.json();
|
||||
|
||||
// Top 100
|
||||
this.frankerToDict(first50json);
|
||||
this.frankerToDict(second50json);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
twitchToDict(json){
|
||||
for (let index = 0; index < json.emotes.length; index++) {
|
||||
|
||||
const { code, id } = json.emotes[index];
|
||||
|
||||
const url = `https://static-cdn.jtvnw.net/emoticons/v1/${id}/3.0`;
|
||||
|
||||
this.dictionary.set(code, new Emote({ code: code, url }));
|
||||
this.twitch_Dictionary.set(code, new Emote({ code: code, url }));
|
||||
}
|
||||
}
|
||||
|
||||
// loadTwitchEmotes is where we collect an object array of emotes from twitch api
|
||||
async loadTwitchEmotes(){
|
||||
|
||||
// https://api.twitchemotes.com/api/v4/channels/0 - twitch globals - 232 items
|
||||
// https://static-cdn.jtvnw.net/emoticons/v1/25/1.0 - cdn
|
||||
|
||||
// Global
|
||||
const twitch_global_api_url = "https://api.twitchemotes.com/api/v4/channels/0";
|
||||
const twitch_global_api_response = await fetch(twitch_global_api_url);
|
||||
var twitch_global_Json = await twitch_global_api_response.json();
|
||||
|
||||
this.twitchToDict(twitch_global_Json);
|
||||
}
|
||||
|
||||
// ♥
|
||||
specialEmotes(){
|
||||
|
||||
var emoteObj = {
|
||||
"emotes": [
|
||||
{
|
||||
"code": "wompWTF",
|
||||
"url": "https://static-cdn.jtvnw.net/emoticons/v1/301653066/3.0"
|
||||
},
|
||||
{
|
||||
"code": "wompISeeYou",
|
||||
"url": "https://static-cdn.jtvnw.net/emoticons/v1/301506153/3.0"
|
||||
},
|
||||
{
|
||||
"code": "wompCry",
|
||||
"url": "https://static-cdn.jtvnw.net/emoticons/v1/301506193/3.0"
|
||||
},
|
||||
{
|
||||
"code": "BabyCorona",
|
||||
"url": "https://static-cdn.jtvnw.net/emoticons/v1/301629296/3.0"
|
||||
},
|
||||
{
|
||||
"code": "LEL",
|
||||
"url": "https://static-cdn.jtvnw.net/emoticons/v1/431249/3.0"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
for (let index = 0; index < emoteObj.emotes.length; index++) {
|
||||
const element = emoteObj[index];
|
||||
const { code, url } = emoteObj.emotes[index];
|
||||
this.dictionary.set(code, new Emote({ code: code, url}));
|
||||
this.specialEmotesDictionary.set(code, new Emote({ code: code, url}));
|
||||
}
|
||||
}
|
||||
}// End Emotes
|
||||
|
||||
export default new Emotes;
|
||||
255
src/content/Message.js
Normal file
255
src/content/Message.js
Normal file
@@ -0,0 +1,255 @@
|
||||
import Emotes from './Emotes';
|
||||
import PersistentSyncStorage from 'src/helpers/PersistentSyncStorage';
|
||||
|
||||
var colorNumberIndex = 0;
|
||||
|
||||
class Message {
|
||||
constructor(messageNode) {
|
||||
this.node = messageNode;
|
||||
this.id = this.node.id; // this.id should not be used to reference the node, dom id changes due to optimisitc updates
|
||||
this.hasEmotes = null;
|
||||
this.observer = null;
|
||||
this.parsedText = ''; // This should be fine since you can't edit/change messages
|
||||
|
||||
this.parseText();
|
||||
|
||||
|
||||
|
||||
// Set Defaults
|
||||
this.setDefaultSelections();
|
||||
|
||||
|
||||
if(this.hasEmotes) {
|
||||
this.node.setAttribute('message-id', this.id);
|
||||
this.setHtml();
|
||||
this.watch();
|
||||
}
|
||||
}
|
||||
|
||||
get textNode() {
|
||||
const node = this.node.querySelector('#message');
|
||||
return {
|
||||
node,
|
||||
text: node.innerText
|
||||
};
|
||||
}
|
||||
|
||||
parseText() {
|
||||
const rawWords = this.textNode.text.split(' ');
|
||||
|
||||
for(let i = 0, length = rawWords.length; i < length; i++) {
|
||||
const word = this.parseIllegalCharcters(rawWords[i]);
|
||||
const emote = Emotes.get(word);
|
||||
|
||||
//console.log(Emotes.get(word));
|
||||
|
||||
|
||||
if(typeof emote === 'undefined') {
|
||||
this.parsedText += word + ' ';
|
||||
} else {
|
||||
this.hasEmotes = true;
|
||||
this.parsedText += emote.html + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch() {
|
||||
this.observer = new MutationObserver(mutations => {
|
||||
let emoteRemoved = false;
|
||||
|
||||
mutations.forEach(mutation => {
|
||||
if(typeof mutation.removedNodes === 'undefined') return;
|
||||
if(mutation.removedNodes.length <= 0) return; // This must be after undefined check
|
||||
|
||||
for(let i = 0, length = mutation.removedNodes.length; i < length; i++) {
|
||||
const removedNode = mutation.removedNodes[i];
|
||||
if(typeof removedNode.className === 'string' && // check if className exists, is 'SVGAnimatedString' when window resized and removed
|
||||
~removedNode.className.indexOf('Emote') !== 0) {
|
||||
emoteRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if(emoteRemoved && document.body.contains(this.node)) {
|
||||
this.setHtml();
|
||||
}
|
||||
});
|
||||
|
||||
this.observer.observe(this.node, {
|
||||
childList: true,
|
||||
attributes: false,
|
||||
characterData: false,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
setHtml() {
|
||||
this.textNode.node.innerHTML = this.parsedText;
|
||||
}
|
||||
|
||||
parseIllegalCharcters(word) {
|
||||
// === 'ZERO WIDTH NO-BREAK SPACE'
|
||||
return word.replace('', '').trim();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if(this.observer !== null) {
|
||||
this.observer.disconnect();
|
||||
this.observer = null;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Setting Options for Each Message
|
||||
setDefaultSelections(){
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Welcome Banner
|
||||
var welcomBanner = document.querySelector("yt-live-chat-viewer-engagement-message-renderer");
|
||||
|
||||
// Set Hide Welcome Banner
|
||||
if (PersistentSyncStorage.data.options.hideWelcomBanner) {
|
||||
welcomBanner.classList.add("hideElement");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Checks for kappa and replaces emoji element with kappa
|
||||
if(PersistentSyncStorage.data.options.kappaFix) {
|
||||
|
||||
var stupidKappa = document.querySelectorAll('#message.yt-live-chat-text-message-renderer .emoji.yt-live-chat-text-message-renderer');
|
||||
|
||||
for (let index = 0; index < stupidKappa.length; index++) {
|
||||
const stupidElement = stupidKappa[index];
|
||||
var stupidToolTip = stupidElement.getAttribute('shared-tooltip-text');
|
||||
|
||||
if(stupidToolTip == ':full_moon_face:'){
|
||||
const newSpan = document.createElement('span');
|
||||
newSpan.classList.add('Emote');
|
||||
newSpan.innerHTML = '<img src="https://static-cdn.jtvnw.net/emoticons/v1/25/3.0" alt="kappa">';
|
||||
|
||||
stupidElement.parentNode.replaceChild(newSpan, stupidElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
//Set Author Colors
|
||||
if(PersistentSyncStorage.data.options.setAuthorColor && this.node.getAttribute('author-type') !== 'owner') {
|
||||
this.setAuthorColor();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Author Icons
|
||||
var author_photo = this.node.querySelector('#author-photo');
|
||||
|
||||
// Set Hide Author Icons
|
||||
if (PersistentSyncStorage.data.options.hideAuthorIcons) {
|
||||
author_photo.classList.add("hideElement");
|
||||
}
|
||||
|
||||
// TimeStamp
|
||||
var timestamp = this.node.querySelector('#timestamp');
|
||||
|
||||
// Set Show TimeStamp
|
||||
if (PersistentSyncStorage.data.options.showTimeStamp) {
|
||||
this.node.classList.add("showTimeStamp");
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Author Name @ auto paste in text area
|
||||
this.node.querySelector('#author-name').addEventListener("click", function(){
|
||||
var inputArea = document.querySelector('#input.yt-live-chat-text-input-field-renderer');
|
||||
var inputAreaLabel = document.querySelector('#label.yt-live-chat-text-input-field-renderer');
|
||||
inputArea.innerText = "@" + this.innerText;
|
||||
const textLength = inputArea.innerText.length;
|
||||
inputArea.focus();
|
||||
inputAreaLabel.innerText = "";
|
||||
});
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set Font Size
|
||||
var textSizeSlider = PersistentSyncStorage.data.options.textSizeSlider;
|
||||
|
||||
if (PersistentSyncStorage.data.options.allowTextSlider) {
|
||||
this.node.setAttribute('style', 'font-size:' + textSizeSlider + 'px' + '!important');
|
||||
this.node.classList.add("AuthorFix");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set Twitch Styling
|
||||
if (PersistentSyncStorage.data.options.setTwitchColors) {
|
||||
this.node.classList.add("setTwitchColors");
|
||||
author_photo.classList.add("hideElement");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set Alternate message Colors
|
||||
if (PersistentSyncStorage.data.options.alternateLineColor) {
|
||||
this.alternateLineColor();
|
||||
}
|
||||
|
||||
}// end setDefaultSelections
|
||||
|
||||
setAuthorColor() {
|
||||
|
||||
let imageSrc = null;
|
||||
|
||||
if(this.node.hasChildNodes && this.node.contains(this.node.querySelector('#author-photo'))){
|
||||
|
||||
if(this.node.querySelector('#author-photo').querySelector('img').src != null){
|
||||
imageSrc = this.node.querySelector('#author-photo').querySelector('img').src;
|
||||
|
||||
const idRegexp = /\/-([A-Za-z-_\d])/;
|
||||
|
||||
try {
|
||||
if(idRegexp.exec(imageSrc) !== null){
|
||||
const parsedSRC = idRegexp.exec(imageSrc)[1];
|
||||
this.node.classList.add(`chat-color-${parsedSRC}`);
|
||||
}
|
||||
} catch (error) {
|
||||
// for some reason nodes from user img.src are getting weird link on occasion
|
||||
console.log(error);
|
||||
console.log(imageSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// changes color every line
|
||||
alternateLineColor(){
|
||||
|
||||
if(colorNumberIndex % 2 == 0){
|
||||
this.node.classList.add("set-background-color-one");
|
||||
}
|
||||
if(colorNumberIndex % 2 !== 0){
|
||||
this.node.classList.add("set-background-color-two");
|
||||
}
|
||||
colorNumberIndex++;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// removes color attr
|
||||
removelternateLineColor(){
|
||||
this.node.classList.remove("set-background-color-one");
|
||||
this.node.classList.remove("set-background-color-two");
|
||||
}
|
||||
|
||||
}// end Message
|
||||
|
||||
|
||||
|
||||
export default Message;
|
||||
39
src/content/RouteWatcher.js
Normal file
39
src/content/RouteWatcher.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import EventEmitter from 'events';
|
||||
|
||||
class RouteWatcher extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.target = document.querySelector('head > title');
|
||||
this.observer = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.observer = new MutationObserver(mutations => {
|
||||
mutations.forEach((m) => {
|
||||
/**
|
||||
* Title is set to 'YouTube Gaming' on main routes
|
||||
* and between routes.
|
||||
*/
|
||||
if(m.target.innerText === 'YouTube Gaming') {
|
||||
this.emit('main');
|
||||
} else {
|
||||
this.emit('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if(this.target !== null) { // Popout chat does not have title tag
|
||||
this.observer.observe(this.target, {
|
||||
childList: true,
|
||||
attributes: false,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RouteWatcher;
|
||||
188
src/content/index.js
Normal file
188
src/content/index.js
Normal file
@@ -0,0 +1,188 @@
|
||||
import "src/stylus/content.styl";
|
||||
import ChatScroller from "./ChatScroller";
|
||||
import ChatWatcher from "./ChatWatcher";
|
||||
import RouteWatcher from "./RouteWatcher";
|
||||
|
||||
|
||||
import {
|
||||
isLivestream, isYoutubeGaming,
|
||||
isYoutubeEmbed, isYoutubeVanilla,
|
||||
isPopOut
|
||||
} from "src/helpers/Identification";
|
||||
|
||||
import PersistentSyncStorage from "src/helpers/PersistentSyncStorage";
|
||||
|
||||
let MAIN = null;
|
||||
const theater_wrapper = document.createElement('theater_wrapper');
|
||||
document.body.appendChild(theater_wrapper);
|
||||
var alreadyTheater = false;
|
||||
|
||||
// ---
|
||||
|
||||
class Main {
|
||||
constructor() {
|
||||
this.chatWatcher = null;
|
||||
this.chatScroller = null;
|
||||
this.routeWatcher = null;
|
||||
this.onRouteChange = this.onRouteChange.bind(this);
|
||||
this.load();
|
||||
|
||||
|
||||
// button class - ytp-size-button ytp-button
|
||||
// right player controls - ytp-right-controls
|
||||
// player div id - ytd-player
|
||||
// chatframe id - chatframe
|
||||
// movieframe id - movie_player_fix
|
||||
// dono ticker id - ticker
|
||||
|
||||
// player-theater-container
|
||||
}
|
||||
|
||||
load() {
|
||||
this.routeWatcher = new RouteWatcher();
|
||||
this.routeWatcher.on("change", this.onRouteChange);
|
||||
this.onRouteChange();
|
||||
}
|
||||
|
||||
onRouteChange() {
|
||||
if(isLivestream() && ((isYoutubeGaming()) || (isYoutubeVanilla()) || (isYoutubeEmbed()) || isPopOut())) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
if(isLivestream()) {
|
||||
if (PersistentSyncStorage.data.options.theaterModeFix) {
|
||||
if(document.getElementById('player-container') != null && document.getElementById('player-theater-container') != null){
|
||||
theaterMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}// end onRouteChange
|
||||
|
||||
init() {
|
||||
|
||||
this.chatWatcher = new ChatWatcher();
|
||||
this.chatWatcher.init();
|
||||
this.chatScroller = new ChatScroller();
|
||||
this.chatScroller.init();
|
||||
|
||||
setDefaults();
|
||||
console.log("INIT");
|
||||
|
||||
}// end init
|
||||
|
||||
}// end main
|
||||
|
||||
|
||||
function setDefaults() {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
//Live Chat Default Option
|
||||
if (PersistentSyncStorage.data.options.setLiveChat) {
|
||||
document.getElementsByClassName("yt-simple-endpoint style-scope yt-dropdown-menu").item(1).click();
|
||||
} else {
|
||||
// do nothing, let user pick option if not set as default in options menu
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
|
||||
// --- Every Frame Loaded
|
||||
PersistentSyncStorage.on("ready", () => {
|
||||
MAIN = new Main();
|
||||
});
|
||||
|
||||
|
||||
function checkMode(){
|
||||
|
||||
if(alreadyTheater){
|
||||
console.log('enterTheater');
|
||||
alreadyTheater = false;
|
||||
enterTheaterMode();
|
||||
}else{
|
||||
// is reverse because at the time of check dom elements havent moved yet
|
||||
if(document.getElementById('player-theater-container').contains(document.getElementById('player-container'))){
|
||||
console.log('exitTheater');
|
||||
exitTheaterMode();
|
||||
}else{
|
||||
console.log('enterTheater');
|
||||
enterTheaterMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enterTheaterMode() {
|
||||
|
||||
const movie_player = document.getElementById('movie_player');
|
||||
const chat_frame = document.getElementById('chatframe');
|
||||
const info_frame = document.getElementById('info-contents');
|
||||
|
||||
const masthead_container = document.getElementById('masthead-container');
|
||||
|
||||
|
||||
masthead_container.hidden = true;
|
||||
|
||||
theater_wrapper.classList.add('theater_wrapper_fix');
|
||||
movie_player.classList.add('movie_player_fix');
|
||||
chat_frame.classList.add('chat_frame_fix');
|
||||
info_frame.classList.add('info_contents_fix');
|
||||
|
||||
|
||||
theater_wrapper.append(info_frame);
|
||||
theater_wrapper.append(movie_player);
|
||||
theater_wrapper.append(chat_frame);
|
||||
|
||||
document.body.classList.add('body_Fix');
|
||||
|
||||
|
||||
}// end enterTheaterMode
|
||||
|
||||
function exitTheaterMode(){
|
||||
|
||||
const movie_player = document.getElementById('movie_player');
|
||||
const chat_frame = document.getElementById('chatframe');
|
||||
const info_frame = document.getElementById('info-contents');
|
||||
|
||||
const movie_player_container = document.getElementById('player-container');
|
||||
const player_container_parent = document.getElementById('player-container-inner');
|
||||
|
||||
const chat_frame_parent = document.getElementById('chat');
|
||||
const info_frame_before = document.getElementById('meta');
|
||||
|
||||
const masthead_container = document.getElementById('masthead-container');
|
||||
|
||||
|
||||
masthead_container.hidden = false;
|
||||
|
||||
theater_wrapper.classList.remove('theater_wrapper_fix');
|
||||
movie_player.classList.remove('movie_player_fix');
|
||||
chat_frame.classList.remove('chat_frame_fix');
|
||||
info_frame.classList.remove('info_contents_fix');
|
||||
|
||||
movie_player_container.prepend(movie_player);
|
||||
player_container_parent.prepend(movie_player_container);
|
||||
chat_frame_parent.prepend(chat_frame);
|
||||
info_frame_before.before(info_frame);
|
||||
|
||||
document.body.classList.remove('body_Fix');
|
||||
}
|
||||
|
||||
function theaterMode(){
|
||||
|
||||
var theaterButton = document.querySelector('button.ytp-size-button.ytp-button');
|
||||
|
||||
if(theaterButton){
|
||||
|
||||
|
||||
if(document.getElementById('player-theater-container').contains(document.getElementById('player-container'))){
|
||||
|
||||
// for when page loads first time - check is reversed after this
|
||||
alreadyTheater = true;
|
||||
checkMode();
|
||||
}
|
||||
|
||||
// add button
|
||||
theaterButton.addEventListener('click', checkMode, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user