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:
wompmacho
2020-11-01 21:02:00 -05:00
parent f47b10a05e
commit 3d81e92ecb
88 changed files with 7717 additions and 0 deletions

View 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
View 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;

View 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
View 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
View 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;

View 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
View 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);
}
}