WES

.win enhancement suite - stable version

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WES
// @namespace    none
// @version      0.1.2.4
// @description  .win enhancement suite - stable version
// @author       melvin
// @include      https://communities.win/*
// @include      https://consumeproduct.win/*
// @include      https://gavinmcinnes.win/*
// @include      https://kotakuinaction.win/*
// @include      https://kotakuinaction2.win/*
// @include      https://omegacanada.win/*
// @include      https://patriots.win/*
// @include      https://weekendgunnit.win/*
// @include      https://greatawakening.win/*
// @include      https://ip2always.win/*
// @include      https://tuckercarlson.win/*
// @include      https://conspiracies.win/*
// @resource     TEST_CSS https://raw.githubusercontent.com/ilovestaples/wes/master/wes/css/settingscss.css
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.5.1.min.js
// @connect      api.imgur.com
// ==/UserScript==

(function() {
    'use strict';

    console.log("WES " + GM.info.script.version);

    /************************ 0. Setup ************************/

    var wes = {
        params: {
            expandMedia : false,
            enableBookmarks: false,
        },

        expandMedia : function(media){
            if(media && media.hasAttribute("data-action")){
                media.click();
            }

            $(media).closest('.post').find('.content').first().removeAttr('data-opened');
        }
    }

    var tags = GM.getValue("tags", "{}").then(function(val) {

        console.log(val);
        var userTags = JSON.parse(val);
        var authors = document.getElementsByClassName("author");
        for(var author of authors){

            var user = author.innerText.trim();

            if(userTags[user]){
                var tag = createTag();
                tag.id = "";
                tag.style.color = userTags[user].color;
                tag.style.backgroundColor = userTags[user].backgroundColor;
                tag.textContent = userTags[user].text;
                author.parentElement.insertBefore(tag, author.nextSibling);
                tag.addEventListener("click", function(){userTaggerShowForm(this)});
            }
            else {
                var tagbutton = createTagButton();
                author.appendChild(tagbutton);
                tagbutton.querySelector("button").addEventListener("click", function(e){e.preventDefault(); userTaggerShowForm(this)});
                author.addEventListener("mouseover", function(){ try{this.querySelector(".tag-button-container").style.display = "inline";}catch(error){}});
                author.addEventListener("mouseout", function(){ try{this.querySelector(".tag-button-container").style.display = "none";}catch(error){}});
            }
        }

        tags = userTags;
    });



    /************************ 1. General Enhancers ************************/

    /************************ 1.1. Create navigation bar ************************/

    function generalEnhancerAddNavigationBar() {


        if(!wes.params.enableBookmarks){
            console.log("Navigation bar disabled. Check back in the next update.")
            return;
        }

        var topBarCss = '.tool-bar-link {text-decoration: none;color: white;font-family: sans-serif;font-size: 13px;}.tool-bar-link {color:#fff;-moz-transition: all 0.1s ease-in;-o-transition: all 0.1s ease-in;-webkit-transition: all 0.1s ease-in;transition: all 0.1s ease-in;}.tool-bar-link:hover { color: #ff9d07; text-shadow: 0px 0px 8px #ff9d07;-moz-transition: all 0.1s ease-in;-o-transition: all 0.1s ease-in;-webkit-transition: all 0.1s ease-in; transition: all 0.1s ease-in;}.tool-bar-link:active {color: #ff9d07; text-shadow: 0px 0px 3px #ff9d07;-moz-transition: all 0.1s ease-in;-o-transition: all 0.1s ease-in;-webkit-transition: all 0.1s ease-in; transition: all 0.1s ease-in;}.bar-link{ font-family: sans-serif;font-size: 13px; color: white; text-decoration: none; position: relative;}.bar-link::after{ content: ""; background: white;mix-blend-mode: exclusion; width: calc(90% + 20px); height: 0; position: absolute; bottom: -3px; left: -6px;transition: all .3s cubic-bezier(0.445, 0.05, 0.55, 0.95);}.bar-link:hover::after{height: calc(100% + 8px)}';

        var topBarStyle = document.createElement('style');
        topBarStyle.className = "style-tag";
        document.body.appendChild(topBarStyle);

        var appendedTopBarStyle = document.querySelector(".style-tag");
        if(appendedTopBarStyle){
            appendedTopBarStyle.innerHTML = topBarCss;
        }

        const winsitesArray = ["patriots", "consumeproduct", "conspiracies", "omegacanada", "kotakuinaction", "kotakuinaction2", "gavinmcinnes", "tuckercarlson", "weekendgunnit", "greatawakening", "ip2always", "communities"];

        var topBar = document.createElement('div');
        topBar.className = "top-bar"
        topBar.style.height = "30px";
        topBar.style.backgroundColor = "black";
        topBar.style.width = "auto";
        topBar.style.opacity = 0.5;
        topBar.style.display = "flex";
        topBar.style.alignItems = "center";
        topBar.style.position = "absolute";
        topBar.style.zIndex = 5;

        var linklist = document.createElement('ul');
        linklist.style.listStyle = "none";

        for (var i = 0; i < winsitesArray.length; i++) {

            addWhiteSpaceToList(linklist);

            var newli = document.createElement('li');
            newli.style.display = "inline";
            newli.style.color = "white";
            newli.style.padding = "5px";

            var newlink = document.createElement('a');
            newlink.href = "https://" + winsitesArray[i] + ".win/";
            newlink.innerHTML = winsitesArray[i];
            newlink.className = "bar-link";
            newlink.padding = "10px"

            newli.appendChild(newlink);
            linklist.appendChild(newli);

            addWhiteSpaceToList(linklist);

            if (i < winsitesArray.length - 1) {
                addDividerToList(linklist)
            }
        }

        topBar.appendChild(linklist);

        var wrapper = document.getElementsByClassName("wrapper");
        var header = document.getElementsByClassName("header");

        if(wrapper[0] && header[0]){
            wrapper[0].insertBefore(topBar, header[0]);
        }

        var themeSwitcher = document.querySelector(".theme-switcher");
        if(themeSwitcher){
            themeSwitcher.style.zIndex = 1;
        }

    }

    /************************ 1.2 Post/comment point redirection ************************/

    function generalEnhancerAddPointRedirection(){
        var navuser = document.querySelector(".nav-user.active");
        if(navuser){

          var username = navuser.children[0].children[0].text;
          var postKarma = document.createElement('a');
          var commentKarma = document.createElement('a');

          postKarma.href = "https://" + window.location.hostname +"/u/" + username + "/?type=post";
          commentKarma.href = "https://" + window.location.hostname +"/u/" + username + "/?type=comment";

          var pointsText = navuser.children[0].childNodes[2];
          var pointsTextContent = pointsText.textContent;

          var postKarmaText = pointsTextContent.substring(pointsTextContent.indexOf("(") + 1, pointsTextContent.indexOf("•") - 1);
          var commentKarmaText = pointsTextContent.substring(pointsTextContent.indexOf("•") + 2, pointsTextContent.indexOf(")"));

          postKarma.innerHTML = postKarmaText;
          postKarma.title = "submitted"
          commentKarma.innerHTML = commentKarmaText;
          commentKarma.title = "comments";

          var sp = document.createElement('span');
          var text1 = document.createElement('text');
          var text2 = document.createElement('text');
          var text3 = document.createElement('text');

          text1.textContent = "(";
          text2.textContent = " • ";
          text3.textContent = ")";

          sp.appendChild(text1);
          sp.appendChild(postKarma);
          sp.appendChild(text2);
          sp.appendChild(commentKarma);
          sp.appendChild(text3);

          navuser.children[0].insertBefore(sp, pointsText);
          navuser.children[0].removeChild(pointsText);
        }
    }

    /************************ 1.3 Settings ************************/

    function displaySettings(){

        var overlay = document.getElementById("overlay");
        overlay.style.display = "flex";
        overlay.style.zIndex = "999";

        var menu = document.getElementById("wes-settings-menu");
        menu.style.display = "block";
        menu.style.zIndex = "1000";

        loadTagsTable();
    }


    function hideSettingsOut(){
        if (!$('#wes-settings-menu').is(':hover')) {
            hideSettings();
        }
    }

    function hideSettings(){

        var overlay = document.getElementById("overlay");
        overlay.style.display = "none";
        overlay.style.zIndex = "-999";

        var menu = document.getElementById("wes-settings-menu");
        menu.style.display = "none";
        menu.style.zIndex = "-1000";

        unloadTagsTable();
    }

    function showGeneralSettings(){
        var sections = document.body.querySelectorAll(".section-container");
        for(var section of sections){section.style.display = "none";}
        document.getElementById("settings-general-sc").style.display = "block";

    }

    function showTagSettings(){
        var sections = document.body.querySelectorAll(".section-container");
        for(var section of sections){section.style.display = "none";}
        document.getElementById("settings-tags-sc").style.display = "block";
    }

    function addSettings(){

        var overlay = document.createElement("div");
        overlay.id = "overlay";
        overlay.style.position = "fixed";
        overlay.style.display = "none";
        overlay.style.zIndex = "-999";
        overlay.style.width = "100%";
        overlay.style.height = "100%";
        overlay.style.top = "0";
        overlay.style.left = "0";
        overlay.style.right = "0";
        overlay.style.bottom = "0";
        overlay.style.backgroundColor = "rgba(0,0,0,0.5)";

        document.body.appendChild(overlay);

        var settingsMenu = document.createElement("div");
        settingsMenu.id = "wes-settings-menu";
        settingsMenu.style.display = "none";
        settingsMenu.style.zIndex = "-1000";
        settingsMenu.style.width = "900px";
        settingsMenu.style.height = "600px";
        settingsMenu.style.backgroundColor = "#444a4f";

        overlay.appendChild(settingsMenu);

        var settingsCSS = document.createElement("style");
        settingsCSS.innerHTML = 'dialog {padding: 10px;}nav {padding: 10px; display: flex;justify-content: space-between;}.sections-list {color: white;font-size: small}.credits {color: white;font-size: small;display: flex;}.section-container {padding: 10px;}fieldset {border: 1px solid;border-radius: 3px;padding: 0.35em 0.625em;margin: 0px 2px;border-color: gray;}legend {color: white;padding: 1px;font-size: smaller;}.svg-inline--fa.fa-times.fa-w-11.close {cursor: pointer;}#utTable {border-collapse: collapse;border: 1px double gray;font-size: smaller;}td {border: 1px double gray;padding: 3px;}.settings-button:hover {text-decoration: underline;}.section-container {height: 750px;overflow: auto;}';
        var settingsMainHTML = '<div id="wes-settings" class="dialog"> <nav> <div class="sections-list"><a class="tab-general settings-button" href="javascript:;">General</a> | <a class="tab-tags settings-button" href="javascript:;">Tagged Users</a> </div> <div class="credits"> <a class="settings-button" href="https://github.com/ilovestaples/wes/" target="_blank">WES</a>&nbsp;|&nbsp; <a class="settings-button" id="wes-menu-version" href="https://github.com/ilovestaples/wes/blob/master/CHANGELOG.md" target="_blank">version_number</a>&nbsp;|&nbsp; <a class="settings-button" href="https://gitreports.com/issue/ilovestaples/wes" target="_blank">Issues</a>&nbsp;|&nbsp; <span id="settings-close" style="cursor: pointer;" class="settings-button" title="Close"><span class="fa fa-times close"></span></span> </div> </nav> <div id="settings-general-sc" class="section-container"> <section class="section-advanced"> <fieldset> <legend>General Settings</legend> <span style="font-size: smaller; color: white;">In future updates, user customizable settings will be here.</span> </fieldset> </section> </div> <div id="settings-tags-sc" class="section-container"> <section class="section-advanced"> <fieldset> <legend>Tagged Users</legend> <table id="utTable" style="width:100%"> </table> </fieldset> </section> </div></div>';

        const my_css = GM_getResourceText("TEST_CSS");
        GM_addStyle(my_css);

        document.body.appendChild(settingsCSS);
        settingsMenu.innerHTML = settingsMainHTML;

        document.getElementById("wes-menu-version").innerHTML = GM.info.script.version;
        document.getElementById("settings-tags-sc").style.display = "none";
        document.getElementById("settings-tags-sc").style.overflow = "auto";
        document.getElementById("settings-tags-sc").style.height = "500px";
        document.body.querySelector(".tab-general").addEventListener("click", showGeneralSettings);
        document.body.querySelector(".tab-tags").addEventListener("click", showTagSettings);
        document.getElementById("settings-close").addEventListener("click", hideSettings);

    }

    function settingsButton(){
        var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttributeNS(null, "aria-hidden", true);
        svg.setAttributeNS(null, "focusable", false);
        svg.setAttributeNS(null, "preserveAspectRatio", "xMidYMid meet");
        svg.style = "vertical-align: -0.3em;-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg); cursor: pointer;";
        svg.setAttributeNS(null, "class", "delete-tag-button");
        svg.setAttributeNS(null, "width", "1.0em");
        svg.setAttributeNS(null, "height", "1.0em");
        svg.setAttributeNS(null, "viewBox", "0 0 512 512");

        var path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path1.setAttributeNS(null, "fill","currentColor");
        path1.setAttributeNS(null, "d","M507.73 109.1c-2.24-9.03-13.54-12.09-20.12-5.51l-74.36 74.36l-67.88-11.31l-11.31-67.88l74.36-74.36c6.62-6.62 3.43-17.9-5.66-20.16c-47.38-11.74-99.55.91-136.58 37.93c-39.64 39.64-50.55 97.1-34.05 147.2L18.74 402.76c-24.99 24.99-24.99 65.51 0 90.5c24.99 24.99 65.51 24.99 90.5 0l213.21-213.21c50.12 16.71 107.47 5.68 147.37-34.22c37.07-37.07 49.7-89.32 37.91-136.73zM64 472c-13.25 0-24-10.75-24-24c0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z");

        var label = document.createElement("label");
        label.className = "settings-button-label";

        var button = document.createElement("button");
        button.style.display = "none";

        var container = document.createElement("span");
        container.className = "settings-button-container";
        container.style.display = "block";

        svg.appendChild(path1);
        label.appendChild(svg);
        label.appendChild(button);
        container.appendChild(label);
        return container;
    }

    function generalEnhancerAddSettingsButton(){

        var navuser = document.querySelector(".nav-user.active");
        if(navuser){

          addSettings();

          var overlay = document.getElementById("overlay");
          overlay.addEventListener("click", hideSettingsOut);

          var dropdown = document.body.querySelector(".dropdown-content");
          var link = document.createElement("a");
          link.setAttribute("href", "javascript:;");
          link.innerHTML = "WES 👨‍🌾";
          link.addEventListener("click", displaySettings);
          dropdown.insertBefore(link, dropdown.lastElementChild);

        }


    }



    /************************ 2. Main Page Enhancers ************************/

    /************************ 2.1. Create tools bar ************************/

    function mainPageEnhancerAddToolBar(){

        var container = document.querySelector(".container");
        var wrapperHome = document.querySelector(".wrapper.home");

        if(wrapperHome && container){

            var tools_bar = document.createElement('div');
            tools_bar.className = "top-bar"
            tools_bar.style.height = "30px";
            tools_bar.style.backgroundColor = "black";
            tools_bar.style.width = "auto";
            tools_bar.style.opacity = 0.5;
            tools_bar.style.display = "flex";
            tools_bar.style.alignItems = "center";
            tools_bar.style.justifyContent = "center"

            var toolslist = document.createElement('ul');
            toolslist.style.listStyle = "none";

            var newli = document.createElement('li');
            newli.style.display = "inline";
            newli.style.color = "white";

            var newlink = document.createElement('a');
            newlink.href = "#";

            newlink.innerHTML = "expand content";
            newlink.className = "tool-bar-link";
            newlink.padding = "10px";
            newlink.id = "expand-media";

            newli.appendChild(newlink);
            toolslist.appendChild(newli);
            tools_bar.appendChild(toolslist);

            wrapperHome.insertBefore(tools_bar, container);


            /************************ 2.1.1 expand media ************************/

            var expandMediaButton = document.getElementById('expand-media');

            expandMediaButton.onclick = function(){

                var thu = document.getElementsByClassName("thumb");
                var len = thu.length;

                for(var i = 0; i < len /*- 1*/ ; i++){
                    wes.expandMedia(thu[i]);
                }

                wes.params.expandMedia = !wes.params.expandMedia;
            }
        }
    }


    /************************ 2.2 Automatically expand content ************************/

    function mainPageEnhancerAutoExpandMedia(){

        //if im on a main page, expand (or not) content in next pages automatically
        var postList = document.querySelector(".post-list");//maybe find another way of checking if im on a main page
        if(postList){

            const plmoConfig = { attributes: false, childList: true, subtree: false };
            const plmoCallback = function(mutationsList, observer) {
                if(wes.params.expandMedia){
                    for(let mutation of mutationsList){

                        for(let node of mutation.addedNodes){
                            var media = node.querySelector(".thumb");
                            wes.expandMedia(media);
                        }
                    }
                }
            }

            const postListObserver = new MutationObserver(plmoCallback);
            postListObserver.observe(postList, plmoConfig);
        }
    }

    /*************** 2.3 Add Tag Buttons on Infinite Scroll ****************/

    function mainPageEnhancerAddTagButtons(){

        //if im on a main page, expand (or not) content in next pages automatically
        var postList = document.querySelector(".post-list");//maybe find another way of checking if im on a main page
        if(postList){

            const plmoConfig = { attributes: false, childList: true, subtree: false };
            const plmoCallback = function(mutationsList, observer) {
                for(let mutation of mutationsList){
                    for(let node of mutation.addedNodes){
                        var author = node.querySelector(".author");

                        //refactor this some day
                        var user = author.innerText.trim();
                        if(tags[user]){
                            var tag = createTag();
                            tag.id = "";
                            tag.style.color = tags[user].color;
                            tag.style.backgroundColor = tags[user].backgroundColor;
                            tag.textContent = tags[user].text;
                            author.parentElement.insertBefore(tag, author.nextSibling);
                            tag.addEventListener("click", function(){userTaggerShowForm(this)});
                        }
                        else {
                            var tagbutton = createTagButton();
                            author.appendChild(tagbutton);
                            tagbutton.querySelector("button").addEventListener("click", function(){userTaggerShowForm(this)});
                            author.addEventListener("mouseover", function(){ try{this.querySelector(".tag-button-container").style.display = "inline";}catch(error){}});
                            author.addEventListener("mouseout", function(){ try{this.querySelector(".tag-button-container").style.display = "none";}catch(error){}});
                        }
                    }
                }
            }

            const postListObserver = new MutationObserver(plmoCallback);
            postListObserver.observe(postList, plmoConfig);
        }
    }

    /************************ 3. Comments Enhancers ************************/

    /************************ 3.1. Automatic Quotes ************************/

    //return True if a mutated object exists, its class is _className and its mutation type is _mutationType
    function objMutated(m, _className, _mutationType){
        return m.target.className && (!m.target.className.toString().localeCompare(_className)) && m.type === _mutationType;
    }

    function isMessage(m){
        return (!m.target.parentElement.className.toString().localeCompare("message")) || (!m.target.parentElement.parentElement.className.toString().localeCompare("message"));
    }

    function getSelectionText() {
        var text = "";
        if (window.getSelection) {
            text = window.getSelection().toString();
        } else if (document.selection && document.selection.type != "Control") {
            text = document.selection.createRange().text;
        }
        text = text.split("\r\n");
        while(text.indexOf("") > -1){
            text.splice(text.indexOf(""), 1);
        }
        return text;
    }

    function commentsEnhancerAddAutomaticQuotes(){

        //save selected text before replying
        var selectedText = "";
        document.body.onmouseup = function(){
            selectedText = getSelectionText();
        }

        //make a MutationObserver for comments
        const moConfig = { attributes: true, childList: true, subtree: true };
        const moCallback = function(mutationsList, observer) {
            for(let mutation of mutationsList){

                //add expandos when editing a comment
                if(objMutated(mutation, "content", "childList")){
                    if(!mutation.target.querySelector(".inline-expand-content-button")){
                        singleCommentExpandMedia(mutation.target);
                    }
                }

                //add expandos when replying to a post or comment
                if(objMutated(mutation, "comment", "childList")){
                    var child = mutation.addedNodes[0];
                    try{
                        if(!child.querySelector(".inline-expand-content-button")){
                            singleCommentExpandMedia(child.querySelector(".content"));
                        }
                    }catch (error) {console.log(error)}
                }


                //autoquotes
                var commentLines = "";
                var textarea = "";
                var replyFormAdded = objMutated(mutation, "body", "childList");
                var replyFormShown = objMutated(mutation, "form reply active", "attributes");

                //if its the first time the reply button was clicked:
                if(replyFormAdded){
                    if(isMessage(mutation)){
                        commentLines = mutation.target.querySelector('.content').children;
                    }
                    else { commentLines = mutation.target.querySelector('.rendered').children;}
                }

                //if im expanding the reply form
                if(replyFormShown){
                    if(isMessage(mutation)){
                        commentLines = mutation.target.parentElement.querySelector('.content').children;
                    }
                    else { commentLines = mutation.target.parentElement.querySelector('.rendered').children;}
                }

                if(replyFormAdded || replyFormShown){
                    textarea = mutation.target.querySelector("textarea");
                    for(let line of selectedText){
                        for(let commentLine of commentLines){

                            if(commentLine.innerText.includes(line)){
                                textarea.value += "> " + line + "\n\n";
                            }
                        }
                    }

                    //keep this functionality in the 'reply' button for as long as the comment box is being displayed
                    mutation.target.parentElement.querySelector('a[data-action="reply"]').onclick = function(){

                        var selectedTextReplyOnClick = getSelectionText();
                        var textareaReplyOnClick = this.parentElement.parentElement.querySelector("textarea");
                        var commentLinesReplyOnClick = "";

                        if(isMessage(mutation)){
                            commentLinesReplyOnClick = this.parentElement.parentElement.querySelector('.content').children;
                        }
                        else{
                            commentLinesReplyOnClick = this.parentElement.parentElement.querySelector('.rendered').children;
                        }

                        try {
                            for(let line of selectedTextReplyOnClick){
                                for(let commentLine of commentLinesReplyOnClick){

                                    if(commentLine.innerText.includes(line)){
                                        textareaReplyOnClick.value += "> " + line + "\n\n";
                                    }
                                }
                            }
                        }
                        catch (error){
                            console.log(error);
                        }
                    }
                }

                //if im hitting cancel, reset the textbox
                if(objMutated(mutation, "form reply", "attributes")){
                    textarea = mutation.target.querySelector("textarea");
                    textarea.value = "";
                    mutation.target.parentElement.querySelector('a[data-action="reply"]').onclick = "";

                }
            }
        };

        const commentsObserver = new MutationObserver(moCallback);

        var comments = document.getElementsByClassName("comment");
        for (var j = 0; j < comments.length; j++) {
            comments[j].onmousedown = function(){
                commentsObserver.observe(this, moConfig);
            }
        }

        const messagesObserver = new MutationObserver(moCallback); //TODO: Use a different config and callback.

        var messagesToObserve = document.getElementsByClassName("message");
        console.log(messagesToObserve);
        for (var k = 0; k < messagesToObserve.length; k++){
            messagesToObserve[k].onmousedown = function(){
                messagesObserver.observe(this, moConfig);
            }
        }

        /* expando related code, move somewhere else*/
        /*make a mutations observer for the comment list*/

        const moConfig_cl = { attributes: true, childList: true, subtree: true };
        const moCallback_cl = function(mutationsList, observer) {
            for(let mutation of mutationsList){
                var childListMutation = mutation.target.className && (!mutation.target.className.toString().localeCompare("comment-list")) && mutation.type === 'childList';
                if (childListMutation){
                    var addedNodes = mutation.addedNodes;
                    if(!addedNodes[0].className.toString().localeCompare("comment")){
                        singleCommentExpandMedia(addedNodes[0].querySelector(".content"));
                    }

                }
            }
        }

        const commentListObserver = new MutationObserver(moCallback_cl);
        var commentList = document.body.querySelector(".comment-list");
        commentListObserver.observe(commentList, moConfig_cl);

    }

    /************************ 3.2. Expand Content in Comments ************************/

    var mediaLinkDictionary = {};

    /* type checks*/

    function isExpandable(link){
        return isImage(link.href) || isVideoFile(link.href) || isTweet(link.href) || isYoutubeLink(link.href) || isImgurAlbum(link.href) || isImgurGallery(link.href) || isStreamable(link.href) || isBitchute(link.href) || isBannedVideo(link.href) || isMAGAVideo(link.href) || isLBRYVideo(link.href) || isOdyseeVideo(link.href);
    }

    function isImage(url){
        var imageExtensions = [".jpg",".jpeg",".png",".gif",".webp"];
        var length = imageExtensions.length;
        for(var i = 0; i < length; i++){
            if(url.includes(imageExtensions[i])){
                return true;
            }
        }
        return false;
    }

    function isVideoFile(url){
        var videoExtensions = [".mp4",".webm"];
        var length = videoExtensions.length;
        for(var i = 0; i < length; i++){
            if(url.includes(videoExtensions[i])){
                return true;
            }
        }
        return false;
    }

    function isTweet(url){
        return !url.search("(https:\/\/twitter.com\/.*?\/(status)\/)");
    }

    function isYoutubeLink(url){
        var ytregexp = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/gi;
        return url.search(ytregexp) >= 0;
    }

    function isImgurAlbum(url){
        var imgurAlbumRegExp = /imgur.com\/a\/.{0,20}/;
        return url.search(imgurAlbumRegExp) >= 0;
    }

    function isImgurGallery(url){
        var imgurGalleryRegExp = /imgur.com\/gallery\/.{0,20}/;
        return url.search(imgurGalleryRegExp) >= 0;
    }

    function isStreamable(url){
        var streamableRegex = /https:\/\/streamable\.com\/.*/;
        return url.search(streamableRegex) >= 0;
    }

    function isBitchute(url){
        var bitchuteRegex = /https:\/\/(www.)*bitchute\.com\/video\/.*/;
        return url.search(bitchuteRegex) >= 0;
    }

    function isBannedVideo(url){
        var bannedVideoRegex = /https:\/\/banned\.video\/watch\?.*/;
        return url.search(bannedVideoRegex) >= 0;
    }

    function isMAGAVideo(url){
        var MAGAVideoRegex = /https:\/\/video\.maga\.host\/videos\/watch\/.*/;
        return url.search(MAGAVideoRegex) >= 0;
    }

    function isLBRYVideo(url){
        var LBRYVideoRegex = /https:\/\/lbry\.tv\/@.*\:.*\/.*/;
        return url.search(LBRYVideoRegex) >= 0;
    }

    function isOdyseeVideo(url){
        var OdyseeVideoRegex = /https:\/\/odysee\.com\/@.*\:.*\/.*/;
        return url.search(OdyseeVideoRegex) >= 0;
    }

    function getYoutubeId(url){
        var ID = '';
        url = url.replace(/(>|<)/gi,'').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
        if(url[2] !== undefined) {
            ID = url[2].split(/[^0-9a-z_\-]/i);
            ID = ID[0];
        }
        else {
            ID = url;
        }
        return ID;
    }

    /* imgur */

    function alterImgurAlbumInterface(imgurAlbumMain, albumData) {

        console.log(albumData);
        if(albumData.data.title){imgurAlbumMain.querySelector(".expando-imgur-album-title").innerHTML = '\"' + albumData.data.title + '\"';}
        imgurAlbumMain.querySelector(".expando-imgur-album-size").innerHTML = albumData.data.images_count;
        imgurAlbumMain.querySelector(".expando-imgur-album-image-number").innerHTML = 1;


        var imgurAlbumImageLinks = [];
        for(var media of albumData.data.images){
          imgurAlbumImageLinks.push(media.link);
        }


        var imgurAlbumContent = imgurAlbumMain.querySelector(".expando-imgur-album-content");

        if(isImage(imgurAlbumImageLinks[0])){
            imgurAlbumContent.appendChild(createImageElement(imgurAlbumImageLinks[0]));
        }
        else if(isVideoFile(imgurAlbumImageLinks[0])){
            imgurAlbumContent.appendChild(createVideoElement(imgurAlbumImageLinks[0]));
        }


        var index = 0;
        var buttons = imgurAlbumMain.querySelectorAll("button");

        buttons[0].onclick = function(){
          if(index > 0){
            var link = imgurAlbumImageLinks[--index];
          }
          else{
            index = imgurAlbumImageLinks.length-1;
            link = imgurAlbumImageLinks[index];
            this.innerHTML = "Previous";
            buttons[1].innerHTML = "First";
          }
            if(index < imgurAlbumImageLinks.length-1){buttons[1].innerHTML = "Next";}

            var media = undefined;
            if(isImage(link)){media = createImageElement(link);}
            else if(isVideoFile(link)){media = createVideoElement(link);}

            imgurAlbumMain.querySelector(".expando-imgur-album-image-number").innerHTML = index + 1;

            imgurAlbumContent.removeChild(imgurAlbumContent.firstElementChild);
            imgurAlbumMain.querySelector(".expando-imgur-album-content").appendChild(media);

            if(index == 0){
                this.innerHTML = "Last";
            }
        }


        buttons[1].onclick = function(){
          if(index < imgurAlbumImageLinks.length-1){
            var link = imgurAlbumImageLinks[++index];
          }
          else{
              index = 0;
              link = imgurAlbumImageLinks[index];
              this.innerHTML = "Next";
              buttons[0].innerHTML = "Last";
          }
            if(index > 0){buttons[0].innerHTML = "Previous";}

            var media = undefined;
            if(isImage(link)){media = createImageElement(link);}
            else if(isVideoFile(link)){media = createVideoElement(link);}

            imgurAlbumMain.querySelector(".expando-imgur-album-image-number").innerHTML = index + 1;
            imgurAlbumContent.removeChild(imgurAlbumContent.firstElementChild);
            imgurAlbumMain.querySelector(".expando-imgur-album-content").appendChild(media);

            if(index == imgurAlbumImageLinks.length-1){
                this.innerHTML = "First";
            }

        }
    }

    function expandoImgurAlbumInterfaceCss(){
        var imgurAlbumInterfaceCss = document.createElement("style");
        imgurAlbumInterfaceCss.innerHTML = ".expandoNavigationBtn { display: inline-block; text-align: center; text-decoration: none; margin: 1px 0; border: solid 1px transparent; border-radius: 4px; padding: 0.2em 0.2em; color: #ffffff; background-color: black;}.expandoNavigationBtn:active { transform: translateY(1px); filter: saturate(150%);}.expandoNavigationBtn:hover { color: black; border-color: currentColor; background-color: white;}.expandoNavigationBtn:focus { outline: none;}.expandoNavigationBtn { outline: none;}.expandoNavigationBtn::-moz-focus-inner { border: none;}";
        document.body.appendChild(imgurAlbumInterfaceCss);

    }

    function expandoImgurAlbumCreateImage(url){
        var img = document.createElement("img");
        img.src = url;
        img.style.maxWidth = "1000px";
        return img;
    }

    function createImgurAlbumInterface(){

        var baseName = "expando-imgur-album";

        var expandoImgurAlbumMain = document.createElement("div");
        expandoImgurAlbumMain.id = "expando-imgur-album-main";

        var expandoImgurAlbumControls = document.createElement("div");
        var expandoImgurAlbumTitle = document.createElement("div");
        expandoImgurAlbumTitle.className = "expando-imgur-album-title";

        var expandoImgurAlbumSize = document.createElement("div");
        var expandoImgurInlineAlbumSize = document.createElement("span");
        expandoImgurInlineAlbumSize.className = baseName + "-size";

        var expandoImgurInlineAlbumImageNumber = document.createElement("span");
        expandoImgurInlineAlbumImageNumber.className = baseName + "-image-number";

        var expandoImgurAlbumContent = document.createElement("div");
        expandoImgurAlbumContent.className = "expando-imgur-album-content";

        var expandoImgurPreviousButton = document.createElement("button");
        expandoImgurPreviousButton.innerHTML = "Last";
        expandoImgurPreviousButton.className = "expandoNavigationBtn";

        var expandoImgurNextButton = document.createElement("button");
        expandoImgurNextButton.innerHTML = "Next";
        expandoImgurNextButton.className = "expandoNavigationBtn";

        //expandoImgurAlbumSize.appendChild(document.createTextNode("Showing "));
        expandoImgurAlbumSize.appendChild(expandoImgurInlineAlbumImageNumber);
        expandoImgurAlbumSize.appendChild(document.createTextNode(" of "));
        expandoImgurAlbumSize.appendChild(expandoImgurInlineAlbumSize);

        expandoImgurAlbumControls.appendChild(expandoImgurPreviousButton);
        expandoImgurAlbumControls.appendChild(expandoImgurNextButton);

        expandoImgurAlbumMain.appendChild(expandoImgurAlbumTitle);
        expandoImgurAlbumMain.appendChild(expandoImgurAlbumSize);
        expandoImgurAlbumMain.appendChild(expandoImgurAlbumControls);
        expandoImgurAlbumMain.appendChild(expandoImgurAlbumContent);

        return expandoImgurAlbumMain;
    }

    /* create media */

    function createImageElement(src){
        var media = document.createElement("img");
        media.src = src;
        return media;
    }

    function createVideoElement(src){
        var media = document.createElement("video");
        media.src = src;
        var extlength = media.src.length - media.src.lastIndexOf(".") - 1;
        var type = media.src.slice(-extlength);
        media.setAttribute("type", "video/" + type);
        media.setAttribute("controls","");
        return media;
    }

    function createTweetElement(src){
        var tweetlink = document.createElement("a");
        tweetlink.href = src;

        var media = document.createElement("blockquote");
        media.className = "twitter-tweet";
        media.appendChild(tweetlink);
        return media;
    }

    function createNullElement(src){
        return null;
    }

    /* finishers */

    function twitterWidget(src, div){
        var scr = document.createElement("script");
            scr.id = "tweet-script";
            scr.charset = "utf-8";
            scr.src= "https://platform.twitter.com/widgets.js";
            scr.async = true;
            document.body.appendChild(scr);
    }

    function youtubeWidget(src, div){
        var videoId = getYoutubeId(src);
        if(src.includes("t=")){
            var split = src.split("=");
            var startTime = split[split.length-1].split("s")[0];
            videoId += "?start=" + startTime;
        }

        var obj = {"video": {"value": "<iframe title='YouTube video player' type=\"text/html\" width='640'  height='390' src='https://www.youtube.com/embed/" + videoId + "' frameborder='0' allowFullScreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function imgurAlbumGalleryFinisher(src, div){
        var album = mediaLinkDictionary[src];
        if(album.data.images_count > 2){
            expandoImgurAlbumInterfaceCss();
            alterImgurAlbumInterface(div, album);
        }
    }

    function streamableWidget(src, div){
        var split = src.split("/");
        var videoid = split[split.length-1];

        var obj = {"video": {"value": "<div style='width: 100%; height: 0px; position: relative; padding-bottom: 56.25%;'><iframe class='streamable-embed' src='https://streamable.com/o/" + videoid + "' frameborder='0' scrolling='no' style='width: 100%; height: 100%; position: absolute;' allowfullscreen></iframe></div>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function bitchuteWidget(src, div){
        var split = src.split("/");
        var videoid = "";

        var last = split[split.length-1];
        if(last.length > 0) {videoid = last}
        else {videoid = split[split.length-2];}

        var obj = {"video": {"value": "<iframe title='Bitchute video player' type=\"text/html\" width='640'  height='390' src='https://www.bitchute.com/embed/" + videoid + "' frameborder='0' allowFullScreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function bannedVideoWidget(src, div){

        var split = src.split("=");
        var videoid = split[split.length-1];

        var obj = {"video": {"value": "<iframe title='Infowars video player' type=\"text/html\" width='640'  height='390' src='https://api.banned.video/embed/" + videoid + "' frameborder='0' allowFullScreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function MAGAVideoWidget(src, div){
        var split = src.split("/");
        var videoid = split[split.length-1];

        var obj = {"video": {"value": "<iframe width='560' height='315' sandbox='allow-same-origin allow-scripts allow-popups' src='https://video.maga.host/videos/embed/" + videoid + "' frameborder='0' allowfullscreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function LBRYVideoWidget(src, div){
        var split = src.split("/");
        var videoid = split[split.length-1];

        var obj = {"video": {"value": "<iframe id='lbry-iframe' width='560' height='315' src='https://lbry.tv/$/embed/" + videoid + "' allowfullscreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function OdyseeVideoWidget(src, div){
        var split = src.split("/");
        var videoid = split[split.length-1];

        var obj = {"video": {"value": "<iframe id='lbry-iframe' width='560' height='315' src='https://odysee.com/$/embed/" + videoid + "' allowfullscreen></iframe>"}};

        $("#"+div.id).html(obj.video.value);
    }

    function noop (){}

    function expandMedia(node){

        var div = document.createElement("div");
        div.style = "resize: both";
        div.id = "id" + Math.random().toString(16).slice(2);
        node.setAttribute("data-media-id", div.id);

        var mediaSource = node.getAttribute("data-media-source");
        var mediaType = node.getAttribute("data-media-type");
        var media = expandos[mediaType].createMedia(mediaSource);

        /*fix this some day*/
        if(!mediaType.localeCompare("imgurAlbum") || !mediaType.localeCompare("imgurGallery")){
            var albumHash = mediaSource.split("/");
            albumHash = albumHash[albumHash.length-1];

            var apiUrl = "https://api.imgur.com/3/album/";
            var albumData = mediaLinkDictionary[mediaSource];

            if(!mediaType.localeCompare("imgurGallery")){apiUrl = "https://api.imgur.com/3/gallery/album/";}


            if(albumData === undefined){
                GM_xmlhttpRequest ( {
                    method: "GET",
                    url: apiUrl + albumHash,
                    headers: {
                        "Authorization": "Client-ID 1d8d9b36339e0e2"
                    },
                    onload: function (response) {
                        if(response.status === 404){
                            media = document.createTextNode("404: not found");
                            if(media){div.appendChild(media);}
                            node.setAttribute("data-media-id", div.id);
                            node.parentElement.insertBefore(div, node.nextSibling);
                            return;
                        }
                        console.log (response.responseText);

                        var reqAlbumData = JSON.parse(response.responseText);
                        mediaLinkDictionary[mediaSource] = reqAlbumData;

                        if(reqAlbumData.data.images_count > 2){
                            media = createImgurAlbumInterface();
                        }
                        else{
                            var singleMedia = reqAlbumData.data.images[0].link;
                            if(isVideoFile(singleMedia)){
                                media = createVideoElement(singleMedia);
                            }
                            else if(isImage(singleMedia)){
                                media = createImageElement(singleMedia);
                            }
                        }

                        if(media){div.appendChild(media);}
                        node.setAttribute("data-media-id", div.id);
                        node.parentElement.insertBefore(div, node.nextSibling);

                        if(reqAlbumData.data.images_count > 2){
                            expandoImgurAlbumInterfaceCss();
                            alterImgurAlbumInterface(div, reqAlbumData);
                        }
                    }
                } );
                return;
            }

            if(albumData.data.images_count > 2){
                media = createImgurAlbumInterface();
            }
            else{
                var singleMedia = albumData.data.images[0].link;
                if(isVideoFile(singleMedia)){
                    media = createVideoElement(singleMedia);
                }
                else if(isImage(singleMedia)){
                    media = createImageElement(singleMedia);
                }
            }
        }

        if(media){div.appendChild(media);}

        node.setAttribute("data-media-id", div.id);
        node.parentElement.insertBefore(div, node.nextSibling);

        expandos[mediaType].finisher(mediaSource, div);

    }


    function contractMedia(node){
        var id = node.getAttribute("data-media-id");
        var media = document.getElementById(id);
        node.parentElement.removeChild(media);
        node.removeAttribute("data-media-id");

    }

    var expandos = {
        image: {
            svgwidth: "1.1em",
            svgheight: "1.1em",
            svgviewBox: "0 -3 35 35",
            svgpath: "M10 4H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h28a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM8.92 8a3 3 0 1 1-3 3a3 3 0 0 1 3-3zM6 27v-4.1l6-6.08a1 1 0 0 1 1.41 0L16 19.35L8.32 27zm24 0H11.15l6.23-6.23l5.4-5.4a1 1 0 0 1 1.41 0L30 21.18z",
            createMedia: createImageElement,
            finisher: noop,
            },
        video: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createVideoElement,
            finisher: noop,
            },
        tweet: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 60 60",
            svgpath: "M60 16l-6 1l4-5l-7 2c-9-10-23 1-19 10C16 24 8 12 8 12s-6 9 4 16l-6-2c0 6 4 10 11 12h-7c4 8 11 8 11 8s-6 5-17 5c33 16 53-14 50-30z",
            createMedia: createTweetElement,
            finisher: twitterWidget,
            },
        youtubeVideo: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: youtubeWidget,
            },
        imgurAlbum: {
            svgwidth: "1.1em",
            svgheight: "1.1em",
            svgviewBox: "0 -3 35 35",
            svgpath: "M10 4H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h28a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM8.92 8a3 3 0 1 1-3 3a3 3 0 0 1 3-3zM6 27v-4.1l6-6.08a1 1 0 0 1 1.41 0L16 19.35L8.32 27zm24 0H11.15l6.23-6.23l5.4-5.4a1 1 0 0 1 1.41 0L30 21.18z",
            createMedia: createNullElement,
            finisher: imgurAlbumGalleryFinisher,
            },
        imgurGallery: {
            svgwidth: "1.1em",
            svgheight: "1.1em",
            svgviewBox: "0 -3 35 35",
            svgpath: "M10 4H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h28a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM8.92 8a3 3 0 1 1-3 3a3 3 0 0 1 3-3zM6 27v-4.1l6-6.08a1 1 0 0 1 1.41 0L16 19.35L8.32 27zm24 0H11.15l6.23-6.23l5.4-5.4a1 1 0 0 1 1.41 0L30 21.18z",
            createMedia: createNullElement,
            finisher: imgurAlbumGalleryFinisher,
            },
        streamable: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: streamableWidget,
            },
        bitchute: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: bitchuteWidget,
            },
        bannedVideo: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: bannedVideoWidget,
            },
        MAGAVideo: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: MAGAVideoWidget,
            },
        LBRYVideo: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: LBRYVideoWidget,
            },
        OdyseeVideo: {
            svgwidth: "1.2em",
            svgheight: "1.2em",
            svgviewBox: "0 -3 20 20",
            svgpath: "M19 15V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2zM8 14V6l6 4z",
            createMedia: createNullElement,
            finisher: OdyseeVideoWidget,
            }
    }

    function getMediaType(url){
        if(isImage(url)){return "image";}
        else if (isVideoFile(url)){return "video";}
        else if (isTweet(url)){return "tweet";}
        else if (isYoutubeLink(url)){return "youtubeVideo";}
        else if (isImgurAlbum(url)){return "imgurAlbum";}
        else if (isImgurGallery(url)){return "imgurGallery";}
        else if (isStreamable(url)){return "streamable";}
        else if (isBitchute(url)){return "bitchute";}
        else if (isBannedVideo(url)){return "bannedVideo";}
        else if (isMAGAVideo(url)){return "MAGAVideo";}
        else if (isLBRYVideo(url)){return "LBRYVideo";}
        else if (isOdyseeVideo(url)){return "OdyseeVideo";}
        return "notMedia";
    }

    function createExpandoButton(url){

        var type = getMediaType(url);
        if (!type.localeCompare("notMedia")){return};

        var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttributeNS(null, "aria-hidden", true);
        svg.setAttributeNS(null, "focusable", false);
        svg.setAttributeNS(null, "preserveAspectRatio", "xMidYMid meet");
        svg.setAttributeNS(null, "class", "inline-expand-content-button");
        svg.setAttributeNS(null, "width", expandos[type].svgwidth);
        svg.setAttributeNS(null, "height", expandos[type].svgheight);
        svg.setAttributeNS(null, "viewBox", expandos[type].svgviewBox);
        svg.style = "-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg); padding-left: 3px; cursor: pointer";

        var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttributeNS(null, "fill","currentColor");
        path.setAttributeNS(null, "d",expandos[type].svgpath);

        var label = document.createElement("label");
        label.className = "expando-checkbox-label";

        var checkbox = document.createElement("input");
        checkbox.type = "checkbox";

        var container = document.createElement("div");
        container.className = "expando-checkbox-container";
        container.setAttribute("data-media-type", type);
        container.setAttribute("data-media-source", url);

        svg.appendChild(path);
        label.appendChild(svg);
        label.appendChild(checkbox);
        container.appendChild(label);

        checkbox.addEventListener( 'change', function() {
            if(this.checked) {
                this.parentElement.querySelector("path").style.fill = "#e7c6ac";
                expandMedia(this.parentElement.parentElement);
            } else {
                this.parentElement.querySelector("path").style.fill = "currentColor";
                contractMedia(this.parentElement.parentElement);
            }
        });

        return container;
    }


    function singleCommentExpandMedia(content){
        var links = content.querySelectorAll("a");
        for(var link of links){
            if(isExpandable(link)){
                link.parentElement.insertBefore(createExpandoButton(link.href), link.nextSibling);
            }
        }

        var style = document.createElement("style");
        style.innerHTML = ".expando-checkbox-container{display: inline-block}.expando-checkbox-label { display: block; position: relative; padding-left: 0px; margin-bottom: 0px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}.expando-checkbox-label input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0;}.expando-checkbox-label:hover input { background-color: #ccc;}.expando-checkbox-label input:checked { display: block;}";
        document.body.appendChild(style);
    }

    function commentsEnhancerExpandMedia(){

        var posts = document.getElementsByClassName("content");

        for(var post of posts){
            var links = post.querySelectorAll("a");
            for(var link of links){
                if(isExpandable(link)){
                    link.parentElement.insertBefore(createExpandoButton(link.href), link.nextSibling);
                }
            }
        }

        var style = document.createElement("style");
        style.innerHTML = ".expando-checkbox-container{display: inline-block}.expando-checkbox-label { display: block; position: relative; padding-left: 0px; margin-bottom: 0px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}.expando-checkbox-label input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0;}.expando-checkbox-label:hover input { background-color: #ccc;}.expando-checkbox-label input:checked { display: block;}";
        document.body.appendChild(style);
    }


    /************************ MAIN ************************/

    generalEnhancerAddNavigationBar();
    generalEnhancerAddPointRedirection();
    generalEnhancerAddSettingsButton();
    mainPageEnhancerAddToolBar();
    mainPageEnhancerAutoExpandMedia();
    mainPageEnhancerAddTagButtons();


    //if im viewing a post:
    if(document.location.href.search("/p/") > 0){
        commentsEnhancerAddAutomaticQuotes();
        commentsEnhancerExpandMedia();
    }

    var userPostsOrCommentsRegex = /\/u\/.*\/\?type=(\bpost\b|\bcomment\b)/;
    var messagesPageRegex = /\/messages/;
    var userPageRegex = /\/u\/.*/;

    if(document.location.href.search(userPageRegex) > 0) {
        commentsEnhancerExpandMedia();
    }

    if (document.location.href.search(messagesPageRegex) > 0){
        commentsEnhancerAddAutomaticQuotes();
    }



    /************************ 7. Update notification ************************/


    //Thank you: https://codepen.io/takaneichinose/pen/eZoZxv

    class MessageBox {
        constructor(id, option) {
            this.id = id;
            this.option = option;
        }

        show(msg, label = "Close", callback = null) {
            if (this.id === null || typeof this.id === "undefined") {
                // if the ID is not set or if the ID is undefined

                throw "Please set the 'ID' of the message box container.";
            }

            if (msg === "" || typeof msg === "undefined" || msg === null) {
                // If the 'msg' parameter is not set, throw an error

                throw "The 'msg' parameter is empty.";
            }

            if (typeof label === "undefined" || label === null) {
                // Of the label is undefined, or if it is null

                label = "Close";
            }

            let option = this.option;

            let msgboxArea = document.querySelector(this.id);
            let msgboxBox = document.createElement("DIV");
            let msgboxContent = document.createElement("DIV");
            let msgboxClose = document.createElement("A");

            if (msgboxArea === null) {
                // If there is no Message Box container found.

                throw "The Message Box container is not found.";
            }

            // Content area of the message box
            msgboxContent.classList.add("msgbox-content");
            msgboxContent.innerHTML = msg;

            // Close button of the message box
            msgboxClose.classList.add("msgbox-close");
            msgboxClose.setAttribute("href", "#");
            msgboxClose.innerText = label;

            // Container of the Message Box element
            msgboxBox.classList.add("msgbox-box");
            msgboxBox.appendChild(msgboxContent);

            if (option.hideCloseButton === false
                || typeof option.hideCloseButton === "undefined") {
                // If the hideCloseButton flag is false, or if it is undefined

                // Append the close button to the container
                msgboxBox.appendChild(msgboxClose);
            }

            msgboxArea.appendChild(msgboxBox);

            msgboxClose.addEventListener("click", (evt) => {
                evt.preventDefault();

                if (msgboxBox.classList.contains("msgbox-box-hide")) {
                    // If the message box already have 'msgbox-box-hide' class
                    // This is to avoid the appearance of exception if the close
                    // button is clicked multiple times or clicked while hiding.

                    return;
                }

                this.hide(msgboxBox, callback);
            });

            if (option.closeTime > 0) {
                this.msgboxTimeout = setTimeout(() => {
                    this.hide(msgboxBox, callback);
                }, option.closeTime);
            }
        }

        hide(msgboxBox, callback) {
            if (msgboxBox !== null) {
                // If the Message Box is not yet closed

                msgboxBox.classList.add("msgbox-box-hide");
            }

            msgboxBox.addEventListener("transitionend", () => {
                if (msgboxBox !== null) {
                    // If the Message Box is not yet closed

                    msgboxBox.parentNode.removeChild(msgboxBox);

                    clearTimeout(this.msgboxTimeout);

                    if (callback !== null) {
                        // If the callback parameter is not null
                        callback();
                    }
                }
            });
        }
    }

    // Creation of Message Box class, and the sample usage
    let msgboxbox = new MessageBox("#msgbox-area", {
        closeTime: 7000,
        hideCloseButton: false
    });


    var version = GM.getValue("wes_version");
    version.then(function(result) {

        var compareVersions = GM.info.script.version.localeCompare(result);

        if(compareVersions){

            var notificationCss = '.msgbox-area { max-height: 100%; position: fixed; bottom: 15px; left: 20px; right: 20px;}.msgbox-area .msgbox-box { font-size: inherit; color: #ffffff; background-color: rgba(0, 0, 0, 0.8); padding: 18px 20px; margin: 0 0 15px; display: flex; align-items: center; position: relative; justify-content: space-between; border-radius: 12px; box-shadow: 0 10px 15px rgba(0, 0, 0, 0.65); transition: opacity 300ms ease-in;}.msgbox-area .msgbox-box.msgbox-box-hide { opacity: 0;}.msgbox-area .msgbox-box:last-child { margin: 0;}.msgbox-area .msgbox-content { flex-shrink: 1;}.msgbox-area .msgbox-close { color: #ffffff; font-weight: bold; text-decoration: none; margin: 0 0 0 20px; flex-grow: 0; flex-shrink: 0; position: relative; transition: text-shadow 225ms ease-out;}.msgbox-area .msgbox-close:hover { text-shadow: 0 0 3px #efefef;}@media (min-width: 481px) and (max-width: 767px) { .msgbox-area { left: 80px; right: 80px; }}@media (min-width: 768px) { .msgbox-area { width: 480px; height: 0; top: 15px; left: auto; right: 15px; }}.msgbox-area { font-size: 16px;}.msgbox-message-container { text-align: center; width: 100vw; height: 100vh; padding: 20px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; flex-direction: column;}.msgbox-message-container h1, .msgbox-message-container h3 { margin: 10px 20px;}.msgbox-message-container p { margin: 5px 20px;}.msgbox-message-button { font-size: 18px; font-weight: bold; font-family: inherit; color: white; background-color: #1476ff; width: 250px; border: solid 2px #005de0; padding: 10px 20px; cursor: pointer; outline: none; box-shadow: 0 5px #005de0; transition: background-color 100ms ease-out, box-shadow 100ms ease-out, transform 100ms ease-out;}.msgbox-message-button:hover, .msgbox-message-button:focus, .msgbox-message-button:active { background-color: #2e85ff;}.msgbox-message-button:active { background-color: #0068fa; box-shadow: 0 0 #005de0; transform: translateY(5px);} .msgbox-link { color: #007BA7; text-decoration: none;}.msgbox-link:hover { text-decoration: underline;}.msgbox-link:active { color: black;}.msgbox-link:visited { color: #007BA7;}';

            var notificationStyle = document.createElement("style");
            notificationStyle.id = "notification-style";
            document.body.appendChild(notificationStyle);

            var appendedNotificationStyle = document.getElementById("notification-style");
            if(appendedNotificationStyle){
                appendedNotificationStyle.innerHTML = notificationCss;
            }

            var messageBoxArea = document.createElement("div");
            messageBoxArea.id = "msgbox-area";
            messageBoxArea.className = "msgbox-area";

            document.body.appendChild(messageBoxArea);


            var message = "WES has been updated to <a class=\"msgbox-link\" href=\"https://github.com/ilovestaples/wes/blob/master/CHANGELOG.md\" target=\"_blank\">version " + GM.info.script.version + "</a>";
            msgboxbox.show(message, null);

            GM.setValue("wes_version",GM.info.script.version);
        }
    });



    /************************ User Tagger *******************************/

    var taggerDialogStyle = document.createElement("style");
    taggerDialogStyle.innerHTML = '.userTagger-dialog {display: flex;flex-direction: column;width: 350px;background-color: white;font-family: sans-serif;font-size: 12px;padding: 2px;}.userTagger-dialog-header {height: 40px;font-size: larger;display: flex;align-items: center;justify-content: space-between;font-weight: bold;background-color: ghostwhite;}.userTagger-dialog-header-container {display: flex;justify-content: space-between;width: 350px;}.userTagger-header-user-container {display: flex;justify-content: space-between;padding-left: 4px;}#userTagger-header-user-username {padding-left: 4px;max-width: 250px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;display: inline-block;}.userTagger-header-controls-container {display: flex;justify-content: space-between;}.userTagger-dialog-controls-container {border: 1px red solid;display: flex;justify-content: space-between;}.WESDialogContents{ padding: 5px;}.fieldPair {display: flex;align-items: center;padding: 4px 0px 4px 0px;}.fieldPair-label {width: 85px;}.fieldPair-text {min-width: 240px;float: left;}.wes-usertagger-footer {display: flex;justify-content: space-between;align-content: center;align-items: center;}.userTag {padding: 0 4px;border: 1px solid #c7c7c7;border-radius: 3px;font-size: .9em;max-width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;display: inline-block;vertical-align: text-bottom;line-height: normal;margin: 0px 2px 0px 2px;cursor: pointer;}';

    document.body.appendChild(taggerDialogStyle);



    function userTaggerShowForm(obj){

        var pos = cumulativeOffset(obj.parentElement);

        var user = obj.closest(".author");
        if(!user){user = obj.parentElement.querySelector(".author");}

        user = user.textContent.trim();

        var formMarkup = document.createElement("div");
        formMarkup.innerHTML = '<div class="userTagger-dialog" style="position: absolute; top: ' +pos.top+'px; left: '+pos.left+'px; z-index:10"> <h3 class="userTagger-dialog-header"> <div class="userTagger-dialog-header-container"> <div class="userTagger-header-user-container"> <svg aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet" cursor="pointer" style="vertical-align: -0.275em; transform: rotate(360deg);" class="tag-button" width="1.2em" height="1.2em" viewBox="0 0 24 24"> <g transform="translate(24 0) scale(-1 1)"> <g fill="none"> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M2 8v8a1 1 0 0 0 1 1h13.62a1 1 0 0 0 .76-.35l3.428-4a1 1 0 0 0 0-1.3l-3.428-4a1 1 0 0 0-.76-.35H3a1 1 0 0 0-1 1zM0 8v8a3 3 0 0 0 3 3h13.62a3 3 0 0 0 2.278-1.048l3.428-4a3 3 0 0 0 0-3.904l-3.428-4A3 3 0 0 0 16.62 5H3a3 3 0 0 0-3 3z"></path> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M15 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2zm0 2a3 3 0 1 0 0-6a3 3 0 0 0 0 6z"></path> </g> </g> </svg> <span id="userTagger-header-user-username">user</span> </div> <div class="userTagger-header-controls-container"> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> </svg> <button id="tagDialogIgnoreUser" style="display: none;"></button> </label> </span> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label" title="close"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" cursor="pointer" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> <path d="M6 2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6zm7.414 8l2.829 2.828a1 1 0 0 1-1.415 1.415L12 13.414l-2.828 2.829a1 1 0 1 1-1.415-1.415L10.586 12L7.757 9.172a1 1 0 0 1 1.415-1.415L12 10.586l2.828-2.829a1 1 0 0 1 1.415 1.415L13.414 12z" fill="#538cc6"/> <rect x="0" y="0" width="24" height="24" fill="rgba(0, 0, 0, 0)" /> </svg> <button id="tagDialogClose" style="display: none;"></button> </label> </span> </div> </div> </h3> <div data-hover-element="1" class="WESHoverBody WESDialogContents"> <form id="userTaggerToolTip"> <div class="fieldPair"><label class="fieldPair-label" for="userTaggerText">Text</label><input class="fieldPair-text" type="text" id="userTaggerText" value=""></div> <div class="fieldPair"> <label class="fieldPair-label" for="userTaggerColor">Color</label> <select id="userTaggerColor"> <option style="color: inherit; background-color: none" value="none">none</option> <option style="color: black; background-color: aqua" value="aqua">aqua</option> <option style="color: white; background-color: black" value="black">black</option> <option style="color: white; background-color: blue" value="blue">blue</option> <option style="color: white; background-color: cornflowerblue" value="cornflowerblue">cornflowerblue</option> <option style="color: white; background-color: fuchsia" value="fuchsia">fuchsia</option> <option style="color: white; background-color: gray" value="gray">gray</option> <option style="color: white; background-color: green" value="green">green</option> <option style="color: black; background-color: lime" value="lime">lime</option> <option style="color: white; background-color: maroon" value="maroon">maroon</option> <option style="color: white; background-color: navy" value="navy">navy</option> <option style="color: white; background-color: olive" value="olive">olive</option> <option style="color: white; background-color: orange" value="orange">orange</option> <option style="color: white; background-color: orangered" value="orangered">orangered</option> <option style="color: black; background-color: pink" value="pink">pink</option> <option style="color: white; background-color: purple" value="purple">purple</option> <option style="color: white; background-color: red" value="red">red</option> <option style="color: black; background-color: silver" value="silver">silver</option> <option style="color: white; background-color: teal" value="teal">teal</option> <option style="color: black; background-color: white" value="white">white</option> <option style="color: black; background-color: yellow" value="yellow">yellow</option> </select> </div> <div class="fieldPair" style="flex-wrap: wrap"><label class="fieldPair-label" for="userTaggerPreview">Preview</label><span id="userTaggerPreview"><span class="RESUserTag"><a id="userTagPreview" class="userTag" title="set a tag" href="javascript:void 0">&nbsp;</a></span></span></div> <div class="wes-usertagger-footer"><span id="view-tagged-users" style=" color:blue; cursor: pointer">View tagged users</span><input type="button" id="userTaggerSave" value="✓ save tag" formaction=""></div> </form> </div></div>';

        var formJS = document.createElement("script");
        formJS.innerHTML = 'var userTaggerCloseButton = document.body.querySelector("#tagDialogClose");userTaggerCloseButton.addEventListener("click", destroyDialog);var userTaggerText = document.body.querySelector("#userTaggerText");userTaggerText.addEventListener("input", updateTagPreview);var colorSelector = document.body.querySelector("#userTaggerColor");colorSelector.addEventListener("input", updateTagPreview);var saveButton = document.body.querySelector("#userTaggerSave");saveButton.addEventListener("click", saveTag);function createTag() { var tag = document.createElement("span"); tag.className = "userTag"; tag.id = "userTagPreview"; return tag;}function updateTagPreview(e) { console.log(e); if (e.target.id.localeCompare("userTaggerText") === 0) { if (e.target.textLength == 1) { var preview = document.body.querySelector("#userTaggerPreview"); var tagPreview = preview.querySelector(".userTag"); var tagPreviewNew = createTag(); var tagPreviewParent = tagPreview.parentElement; tagPreviewParent.appendChild(tagPreviewNew); tagPreviewParent.removeChild(tagPreview); tagPreviewNew.style.color = colorSelector.selectedOptions[0].style.color; tagPreviewNew.style.backgroundColor = colorSelector.selectedOptions[0].style.backgroundColor; } document.body.querySelector("#userTagPreview").textContent = e.target.value; } else if (e.target.id.localeCompare("userTaggerColor") === 0) { var preview = document.body.querySelector("#userTagPreview"); console.log(e.target.selectedOptions[0].style.color); console.log(e.target.selectedOptions[0].style.backgroundColor); console.log(preview); preview.style.color = e.target.selectedOptions[0].style.color; preview.style.backgroundColor = e.target.selectedOptions[0].style.backgroundColor; }}function saveTag(){ console.log("tag saved"); destroyDialog();}function destroyDialog(){ var dialog = document.querySelector(".userTagger-dialog"); dialog.parentElement.removeChild(dialog);}';

        document.body.appendChild(formMarkup);
        document.getElementById("userTagger-header-user-username").innerHTML = user;

        if(tags[user]){

            var formTagTextInput = document.getElementById("userTaggerText");
            formTagTextInput.value = tags[user].text;

            var formTagColorSelect = document.getElementById("userTaggerColor");
            formTagColorSelect.value = tags[user].backgroundColor;

            var preview = document.getElementById("userTagPreview");
            preview.innerText = tags[user].text;
            preview.style.color = tags[user].color;
            preview.style.backgroundColor = tags[user].backgroundColor;
        }

        formjs();
    }


    /*Tagger dialog code*/
    function createTag() {
        var tag = document.createElement("span");
        tag.className = "userTag";
        tag.id = "userTagPreview";
        return tag;
    }

    function updateTagPreview(e) {

        var colorSelector = document.body.querySelector("#userTaggerColor");

        if (e.target.id.localeCompare("userTaggerText") === 0) {
            if (e.target.textLength == 1) {
                var preview = document.body.querySelector("#userTaggerPreview");
                var tagPreview = preview.querySelector(".userTag");
                var tagPreviewNew = createTag();
                var tagPreviewParent = tagPreview.parentElement;
                tagPreviewParent.appendChild(tagPreviewNew);
                tagPreviewParent.removeChild(tagPreview);
                tagPreviewNew.style.color = colorSelector.selectedOptions[0].style.color;
                tagPreviewNew.style.backgroundColor = colorSelector.selectedOptions[0].style.backgroundColor;
            }
            document.body.querySelector("#userTagPreview").textContent = e.target.value;
        } else if (e.target.id.localeCompare("userTaggerColor") === 0) {
            var rdpreview = document.body.querySelector("#userTagPreview");
            rdpreview.style.color = e.target.selectedOptions[0].style.color;
            rdpreview.style.backgroundColor = e.target.selectedOptions[0].style.backgroundColor;
        }
    }

    /*SAVE HANDLER*/

    function saveTag(){



        var user = document.body.querySelector("#userTagger-header-user-username").innerText.trim();

        var utc = document.body.querySelector("#userTaggerColor");
        var tagData = {
            color : utc.selectedOptions[0].style.color,
            backgroundColor : utc.selectedOptions[0].style.backgroundColor,
            text : document.body.querySelector("#userTagPreview").textContent
        }


        if(!tags[user]){
            console.log("tag doesnt exist");
            var authors = document.body.querySelectorAll(".author");
            for(var author of authors){
                if(!author.innerText.trim().localeCompare(user)){
                    var tbc = author.querySelector(".tag-button-container");
                    author.removeChild(tbc);

                    var tag = createTag();
                    tag.id = "";
                    tag.style.color = tagData.color;
                    tag.style.backgroundColor = tagData.backgroundColor;
                    tag.textContent = tagData.text;
                    author.parentElement.insertBefore(tag, author.nextSibling);
                    tag.addEventListener("click", function(){userTaggerShowForm(this)});
                }
            }
        }



        tags[user] = tagData;
        GM.setValue("tags",JSON.stringify(tags));

        destroyDialog();

        //if user already had a tag, refresh it
        var allTags = document.body.querySelectorAll(".userTag");
        for(var tag of allTags){
            if(!tag.previousSibling.innerText.trim().localeCompare(user)){
                tag.style.color = tags[user].color;
                tag.style.backgroundColor = tags[user].backgroundColor;
                tag.innerText = tags[user].text;
            }
        }
    }

    function destroyDialog(){

        var dialog = document.querySelector(".userTagger-dialog");
        dialog.parentElement.removeChild(dialog);

    }

    function formjs(){
        var userTaggerCloseButton = document.body.querySelector("#tagDialogClose");
        userTaggerCloseButton.addEventListener("click", destroyDialog);

        var userTaggerText = document.body.querySelector("#userTaggerText");
        userTaggerText.addEventListener("input", updateTagPreview);
        var colorSelector = document.body.querySelector("#userTaggerColor");
        colorSelector.addEventListener("input", updateTagPreview);

        var viewTaggedUsers = document.getElementById("view-tagged-users");
        viewTaggedUsers.addEventListener("click", viewTaggedUsers_f);


        /*SAVE*/

        var saveButton = document.body.querySelector("#userTaggerSave");
        saveButton.addEventListener("click", saveTag);
    }

    function viewTaggedUsers_f(){
        displaySettings();
        showTagSettings();
    }

    /*End tagger dialog code*/


    function cumulativeOffset(element) {
        var top = 0, left = 0;
        do {
            top += element.offsetTop || 0;
            left += element.offsetLeft || 0;
            element = element.offsetParent;
        } while(element);

        return {
            top: top,
            left: left + 15
        };
    };

    function createTagButton(){

        var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttributeNS(null, "aria-hidden", true);
        svg.setAttributeNS(null, "focusable", false);
        svg.setAttributeNS(null, "preserveAspectRatio", "xMidYMid meet");
        svg.style = "vertical-align: -0.0em;-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg); cursor: pointer; padding-left: 3px";
        svg.setAttributeNS(null, "class", "tag-button");
        svg.setAttributeNS(null, "width", "1.4em");
        svg.setAttributeNS(null, "height", "0.9em");
        svg.setAttributeNS(null, "viewBox", "5 3 16 16");

        var path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path1.setAttributeNS(null, "fill","#369");
        path1.setAttributeNS(null, "fill-rule","evenodd");
        path1.setAttributeNS(null, "clip-rule","evenodd");
        path1.setAttributeNS(null, "d","M2 8v8a1 1 0 0 0 1 1h13.62a1 1 0 0 0 .76-.35l3.428-4a1 1 0 0 0 0-1.3l-3.428-4a1 1 0 0 0-.76-.35H3a1 1 0 0 0-1 1zM0 8v8a3 3 0 0 0 3 3h13.62a3 3 0 0 0 2.278-1.048l3.428-4a3 3 0 0 0 0-3.904l-3.428-4A3 3 0 0 0 16.62 5H3a3 3 0 0 0-3 3z");

        var path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path2.setAttributeNS(null, "fill","#369");
        path2.setAttributeNS(null, "fill-rule","evenodd");
        path2.setAttributeNS(null, "clip-rule","evenodd");
        path2.setAttributeNS(null, "d","M15 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2zm0 2a3 3 0 1 0 0-6a3 3 0 0 0 0 6z");

        var g1 = document.createElementNS("http://www.w3.org/2000/svg", "g");
        g1.setAttributeNS(null, "transform","translate(24 0) scale(-1 1)");
        var g2 = document.createElementNS("http://www.w3.org/2000/svg", "g");
        g2.setAttributeNS(null, "fill","none");


        var label = document.createElement("label");
        label.className = "tag-button-label";

        var button = document.createElement("button");
        button.style.display = "none";

        var container = document.createElement("span");
        container.className = "tag-button-container";
        container.style.display = "none";


        g2.appendChild(path1);
        g2.appendChild(path2);
        g1.appendChild(g2);
        svg.appendChild(g1);
        label.appendChild(svg);
        label.appendChild(button);
        container.appendChild(label);
        return container;
    }

    function loadTagsTable(){

        GM.getValue("tags", "{}").then(function(val) {

            var userTags = JSON.parse(val);

            var taggedUsersTable = document.getElementById("utTable");

            var thead = document.createElement("thead");
            var thUser = document.createElement("th");
            thUser.innerHTML = "User";
            thUser.style.color = "white";
            thUser.style.width = "45%";

            var thTag = document.createElement("th");
            thTag.innerHTML = "Tag";
            thTag.style.color = "white";
            thTag.style.width = "45%";

            var thActions = document.createElement("th");
            thActions.innerHTML = "Actions";
            thActions.style.color = "white";

            thead.appendChild(thUser);
            thead.appendChild(thTag);
            thead.appendChild(thActions);
            taggedUsersTable.appendChild(thead);

            for (var key in userTags) {

                if (userTags.hasOwnProperty(key)) {

                    var row = taggedUsersTable.insertRow(0);

                    var userCell = row.insertCell(-1);
                    var userText = document.createElement("span");
                    userText.innerText = key.toString();
                    userText.style.color = "white";
                    userCell.appendChild(userText);

                    var tag = createTag();
                    tag.id = "";
                    tag.style.color = userTags[key].color;
                    tag.style.backgroundColor = userTags[key].backgroundColor;
                    tag.textContent = userTags[key].text;
                    tag.addEventListener("click", function(){userTaggerShowFormTable(this)});

                    var tagCell = row.insertCell(-1);
                    tagCell.appendChild(tag);

                    var deleteButton = deleteTagButton();
                    deleteButton.querySelector("button").addEventListener("click", function(){deleteTag(this);});

                    var actionsCell = row.insertCell(-1);
                    actionsCell.appendChild(deleteButton);

                    /*var editButton = editTagButton();
                    editButton.querySelector("button").addEventListener("click", function(){userTaggerShowFormTable(this)});
                    actionsCell.appendChild(editButton);*/
                    actionsCell.style = "text-align: center;";

                }
            }
        });
    }

    function unloadTagsTable(){
        var table = document.getElementById("utTable");
        while (table.lastElementChild) {
            table.removeChild(table.lastElementChild);
        }
    }

    function deleteTag(obj){

        if(confirm("Do you wish to delete this tag?")){
            var user = obj.closest("tr").querySelector("span").innerText;
            delete tags[user];
            GM.setValue("tags",JSON.stringify(tags));

            var tr = obj.closest("tr");
            obj.closest("tbody").removeChild(tr);


            var authors = document.body.querySelectorAll(".author");
            for(var author of authors){
                if(!author.innerText.trim().localeCompare(user)){
                    var tag = author.nextSibling;
                    var tagButton = createTagButton();
                    author.parentElement.removeChild(tag);
                    author.appendChild(tagButton);

                    tagButton.querySelector("button").addEventListener("click", function(){userTaggerShowForm(this)});
                }
            }
        }
    }

    function userTaggerShowFormTable(obj){

        var currentDialog = document.body.querySelector(".userTagger-dialog");
        if(currentDialog){currentDialog.parentElement.removeChild(currentDialog);}

        var div = document.getElementById("settings-tags-sc");

        var pos = cumulativeOffset(obj.closest("tr").querySelector(".userTag"));

        var postop = pos.top - div.scrollTop;

        var user = obj.closest("tr").querySelector("span").innerText;

        var formMarkup = document.createElement("div");
        //formMarkup.innerHTML = '<div class="userTagger-dialog" style="position: absolute; inset: ' +postop+'px auto auto '+pos.left+'px;"> <h3 class="userTagger-dialog-header"> <div class="userTagger-dialog-header-container"> <div class="userTagger-header-user-container"> <svg aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet" cursor="pointer" style="vertical-align: -0.275em; transform: rotate(360deg);" class="tag-button" width="1.2em" height="1.2em" viewBox="0 0 24 24"> <g transform="translate(24 0) scale(-1 1)"> <g fill="none"> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M2 8v8a1 1 0 0 0 1 1h13.62a1 1 0 0 0 .76-.35l3.428-4a1 1 0 0 0 0-1.3l-3.428-4a1 1 0 0 0-.76-.35H3a1 1 0 0 0-1 1zM0 8v8a3 3 0 0 0 3 3h13.62a3 3 0 0 0 2.278-1.048l3.428-4a3 3 0 0 0 0-3.904l-3.428-4A3 3 0 0 0 16.62 5H3a3 3 0 0 0-3 3z"></path> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M15 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2zm0 2a3 3 0 1 0 0-6a3 3 0 0 0 0 6z"></path> </g> </g> </svg> <span id="userTagger-header-user-username">user</span> </div> <div class="userTagger-header-controls-container"> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> </svg> <button id="tagDialogIgnoreUser" style="display: none;"></button> </label> </span> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label" title="close"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" cursor="pointer" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> <path d="M6 2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6zm7.414 8l2.829 2.828a1 1 0 0 1-1.415 1.415L12 13.414l-2.828 2.829a1 1 0 1 1-1.415-1.415L10.586 12L7.757 9.172a1 1 0 0 1 1.415-1.415L12 10.586l2.828-2.829a1 1 0 0 1 1.415 1.415L13.414 12z" fill="#538cc6"/> <rect x="0" y="0" width="24" height="24" fill="rgba(0, 0, 0, 0)" /> </svg> <button id="tagDialogClose" style="display: none;"></button> </label> </span> </div> </div> </h3> <div data-hover-element="1" class="WESHoverBody WESDialogContents"> <form id="userTaggerToolTip"> <div class="fieldPair"><label class="fieldPair-label" for="userTaggerText">Text</label><input class="fieldPair-text" type="text" id="userTaggerText" value=""></div> <div class="fieldPair"> <label class="fieldPair-label" for="userTaggerColor">Color</label> <select id="userTaggerColor"> <option style="color: inherit; background-color: none" value="none">none</option> <option style="color: black; background-color: aqua" value="aqua">aqua</option> <option style="color: white; background-color: black" value="black">black</option> <option style="color: white; background-color: blue" value="blue">blue</option> <option style="color: white; background-color: cornflowerblue" value="cornflowerblue">cornflowerblue</option> <option style="color: white; background-color: fuchsia" value="fuchsia">fuchsia</option> <option style="color: white; background-color: gray" value="gray">gray</option> <option style="color: white; background-color: green" value="green">green</option> <option style="color: black; background-color: lime" value="lime">lime</option> <option style="color: white; background-color: maroon" value="maroon">maroon</option> <option style="color: white; background-color: navy" value="navy">navy</option> <option style="color: white; background-color: olive" value="olive">olive</option> <option style="color: white; background-color: orange" value="orange">orange</option> <option style="color: white; background-color: orangered" value="orangered">orangered</option> <option style="color: black; background-color: pink" value="pink">pink</option> <option style="color: white; background-color: purple" value="purple">purple</option> <option style="color: white; background-color: red" value="red">red</option> <option style="color: black; background-color: silver" value="silver">silver</option> <option style="color: white; background-color: teal" value="teal">teal</option> <option style="color: black; background-color: white" value="white">white</option> <option style="color: black; background-color: yellow" value="yellow">yellow</option> </select> </div> <div class="fieldPair" style="flex-wrap: wrap"><label class="fieldPair-label" for="userTaggerPreview">Preview</label><span id="userTaggerPreview"><span class="RESUserTag"><a id="userTagPreview" class="userTag" title="set a tag" href="javascript:void 0">&nbsp;</a></span></span></div> <div class="wes-usertagger-footer"><input type="button" id="userTaggerSave" value="✓ save tag" formaction=""></div> </form> </div></div>';
        formMarkup.innerHTML = '<div class="userTagger-dialog" style="position: absolute; top: ' +postop+'px; left: '+pos.left+'px;"> <h3 class="userTagger-dialog-header"> <div class="userTagger-dialog-header-container"> <div class="userTagger-header-user-container"> <svg aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet" cursor="pointer" style="vertical-align: -0.275em; transform: rotate(360deg);" class="tag-button" width="1.2em" height="1.2em" viewBox="0 0 24 24"> <g transform="translate(24 0) scale(-1 1)"> <g fill="none"> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M2 8v8a1 1 0 0 0 1 1h13.62a1 1 0 0 0 .76-.35l3.428-4a1 1 0 0 0 0-1.3l-3.428-4a1 1 0 0 0-.76-.35H3a1 1 0 0 0-1 1zM0 8v8a3 3 0 0 0 3 3h13.62a3 3 0 0 0 2.278-1.048l3.428-4a3 3 0 0 0 0-3.904l-3.428-4A3 3 0 0 0 16.62 5H3a3 3 0 0 0-3 3z"></path> <path fill="#538cc6" fill-rule="evenodd" clip-rule="evenodd" d="M15 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2zm0 2a3 3 0 1 0 0-6a3 3 0 0 0 0 6z"></path> </g> </g> </svg> <span id="userTagger-header-user-username">user</span> </div> <div class="userTagger-header-controls-container"> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> </svg> <button id="tagDialogIgnoreUser" style="display: none;"></button> </label> </span> <span class="tag-button-container" style="padding-left: 2px;"> <label class="tag-button-label" title="close"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" cursor="pointer" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"> <path d="M6 2h12a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6zm7.414 8l2.829 2.828a1 1 0 0 1-1.415 1.415L12 13.414l-2.828 2.829a1 1 0 1 1-1.415-1.415L10.586 12L7.757 9.172a1 1 0 0 1 1.415-1.415L12 10.586l2.828-2.829a1 1 0 0 1 1.415 1.415L13.414 12z" fill="#538cc6"/> <rect x="0" y="0" width="24" height="24" fill="rgba(0, 0, 0, 0)" /> </svg> <button id="tagDialogClose" style="display: none;"></button> </label> </span> </div> </div> </h3> <div data-hover-element="1" class="WESHoverBody WESDialogContents"> <form id="userTaggerToolTip"> <div class="fieldPair"><label class="fieldPair-label" for="userTaggerText">Text</label><input class="fieldPair-text" type="text" id="userTaggerText" value=""></div> <div class="fieldPair"> <label class="fieldPair-label" for="userTaggerColor">Color</label> <select id="userTaggerColor"> <option style="color: inherit; background-color: none" value="none">none</option> <option style="color: black; background-color: aqua" value="aqua">aqua</option> <option style="color: white; background-color: black" value="black">black</option> <option style="color: white; background-color: blue" value="blue">blue</option> <option style="color: white; background-color: cornflowerblue" value="cornflowerblue">cornflowerblue</option> <option style="color: white; background-color: fuchsia" value="fuchsia">fuchsia</option> <option style="color: white; background-color: gray" value="gray">gray</option> <option style="color: white; background-color: green" value="green">green</option> <option style="color: black; background-color: lime" value="lime">lime</option> <option style="color: white; background-color: maroon" value="maroon">maroon</option> <option style="color: white; background-color: navy" value="navy">navy</option> <option style="color: white; background-color: olive" value="olive">olive</option> <option style="color: white; background-color: orange" value="orange">orange</option> <option style="color: white; background-color: orangered" value="orangered">orangered</option> <option style="color: black; background-color: pink" value="pink">pink</option> <option style="color: white; background-color: purple" value="purple">purple</option> <option style="color: white; background-color: red" value="red">red</option> <option style="color: black; background-color: silver" value="silver">silver</option> <option style="color: white; background-color: teal" value="teal">teal</option> <option style="color: black; background-color: white" value="white">white</option> <option style="color: black; background-color: yellow" value="yellow">yellow</option> </select> </div> <div class="fieldPair" style="flex-wrap: wrap"><label class="fieldPair-label" for="userTaggerPreview">Preview</label><span id="userTaggerPreview"><span class="RESUserTag"><a id="userTagPreview" class="userTag" title="set a tag" href="javascript:void 0">&nbsp;</a></span></span></div> <div class="wes-usertagger-footer"><input type="button" id="userTaggerSave" value="✓ save tag" formaction=""></div> </form> </div></div>';

        document.body.appendChild(formMarkup);
        document.body.querySelector(".userTagger-dialog").style.zIndex = 1001;

        console.log(user);
        var headerName = document.getElementById("userTagger-header-user-username");
        headerName.textContent = user;

        console.log(document.getElementById("userTagger-header-user-username"));

        if(tags[user]){

            var formTagTextInput = document.getElementById("userTaggerText");
            formTagTextInput.value = tags[user].text;

            var formTagColorSelect = document.getElementById("userTaggerColor");
            formTagColorSelect.value = tags[user].backgroundColor;

            var preview = document.getElementById("userTagPreview");
            preview.innerText = tags[user].text;
            preview.style.color = tags[user].color;
            preview.style.backgroundColor = tags[user].backgroundColor;
        }

        formjsTable();
    }

    function formjsTable(){
        var userTaggerCloseButton = document.body.querySelector("#tagDialogClose");
        userTaggerCloseButton.addEventListener("click", destroyDialog);

        var userTaggerText = document.body.querySelector("#userTaggerText");
        userTaggerText.addEventListener("input", updateTagPreview);
        var colorSelector = document.body.querySelector("#userTaggerColor");
        colorSelector.addEventListener("input", updateTagPreview);


        /*SAVE*/

        var saveButton = document.body.querySelector("#userTaggerSave");
        saveButton.addEventListener("click", saveTagTable);
    }

    function saveTagTable(){

        var user = document.body.querySelector("#userTagger-header-user-username").innerText.trim();

        var utc = document.body.querySelector("#userTaggerColor");
        var tagData = {
            color : utc.selectedOptions[0].style.color,
            backgroundColor : utc.selectedOptions[0].style.backgroundColor,
            text : document.body.querySelector("#userTagPreview").textContent
        }


        if(!tags[user]){
            console.log("tag doesnt exist");
            var authors = document.body.querySelectorAll(".author");
            for(var author of authors){
                if(!author.innerText.trim().localeCompare(user)){
                    var tbc = author.querySelector(".tag-button-container");
                    author.removeChild(tbc);

                    var tag = createTag();
                    tag.id = "";
                    tag.style.color = tagData.color;
                    tag.style.backgroundColor = tagData.backgroundColor;
                    tag.textContent = tagData.text;
                    author.parentElement.insertBefore(tag, author.nextSibling);
                    tag.addEventListener("click", function(){userTaggerShowForm(this)});
                }
            }
        }



        tags[user] = tagData;
        GM.setValue("tags",JSON.stringify(tags));

        destroyDialog();

        //if user already had a tag, refresh it
        var allTags = document.body.querySelector(".container").querySelectorAll(".userTag");
        for(var tag of allTags){
            if(!tag.previousSibling.innerText.trim().localeCompare(user)){
                tag.style.color = tags[user].color;
                tag.style.backgroundColor = tags[user].backgroundColor;
                tag.innerText = tags[user].text;
            }
        }

        unloadTagsTable();
        loadTagsTable();
    }


    function darkThemeOn(){
        return !document.body.className.localeCompare("theme-dark");
    }

    function pickThemeColor(){
        return (darkThemeOn() ? 'white' : 'black');
    }

    function deleteTagButton(){
        var icon = document.createElement("span");
        icon.innerHTML = '<i class="fas fa-times" style=" font-size:large ;color: white; cursor: pointer" title="Remove Tag"></i>';

        var label = document.createElement("label");
        label.className = "delete-tag-button-label";

        var button = document.createElement("button");
        button.style.display = "none";

        var container = document.createElement("span");
        container.className = "delete-tag-button-container";
        container.style.display = "visible";
        container.style.paddingRight = "5px";

        label.appendChild(icon);
        label.appendChild(button);
        container.appendChild(label);
        return container;
    }

    /************************ 8.2 Misc functions ************************/


    //appends a white space to a list
    function addWhiteSpaceToList(list){
        var space = document.createElement('li');
        space.style.display = "inline";
        space.style.color = "white";
        space.innerHTML = "&nbsp"
        list.appendChild(space);
    }

    //appends a pipe '|' to a list
    function addDividerToList(list){
        var divider = document.createElement('li');
        divider.style.display = "inline";
        divider.style.color = "white";
        divider.innerHTML = "|";
        list.appendChild(divider);
    }


})();