[AO3] Mute Selectively

Allow more flexible muting without resorting to a site skin

// ==UserScript==
// @name         [AO3] Mute Selectively
// @namespace    http://tampermonkey.net/
// @version      2.0-beta1
// @description  Allow more flexible muting without resorting to a site skin
// @license      MIT
// @match        *://*.archiveofourown.org/*
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.deleteValue
// ==/UserScript==

// Many thanks to a certain other nonnie

const standardSize = {
    titleFont: 30,
    headerFont: 20,
    tabFont: 19,
    refreshFont: 15,
    actionFont: 14,
    menuWidth: 600,
    menuHeight: 600
}

const mobileSize = {
    titleFont: 24,
    headerFont: 18,
    tabFont: 14,
    refreshFont: 13,
    actionFont: 12,
    menuWidth: 390,
    menuHeight: 500
}

var fontSizing = {}

var commentMute = []

function getMenuRules(darkmode) {

    var rules = {};

    rules['.AO3-MS-toggle'] = {
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
        'background-color': darkmode? '#1f1f1f' : '#f1f1f1',
        'padding': '1px 4px',
        'margin': '0px 0px 0px 4px',
        'display': 'inline-block',
        'border': '1px solid ' + (darkmode? '#f1f1f1' : '#1f1f1f'),
    };

    rules['.AO3-MS-toggle a'] = {
        'text-decoration': 'none',
        'border': (darkmode? '#f1f1f1' : '#1f1f1f') + '!important',
        'color': (darkmode? '#f1f1f1' : '#1f1f1f') + '!important',
    };

    rules['.AO3-MS-usermenu'] = {
        'position': 'absolute',
        'display': 'inline-block',
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
        'background-color': darkmode? '#1f1f1f' : '#f1f1f1',
        'margin': '0px 0px 0px 4px',
        'border-style': 'solid',
        'border-color': darkmode? '#f1f1f1 !important' : '#1f1f1f !important',
        'z-index': '100',
    };

    rules['.AO3-MS-usermenu-item'] = {
        'display': 'block',
        'position': 'relative',
        'padding': '3px 5px 2px 5px',
    };

    rules['.AO3-MS-usermenu-item-selected'] = {
        'background-color': darkmode? '#3f3f3f' : '#d1d1d1',
    };

    rules['.AO3-MS-usermenu-item a'] = {
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
        'text-decoration': 'none',
        'display': 'inline-block',
        'width': '100%',
        'border': darkmode? '#f1f1f1' : '#1f1f1f',
    };

    rules['.AO3-MS-usermenu-item:hover'] = {
        'background-color': darkmode? '#555' : '#ddd',
    };

    rules['.AO3-MS-muted'] = {
        'display': 'none !important',
    };


    rules['.AO3-MS-collapseBlurb'] = {
        'padding': '20px',
    }

    rules['.AO3-MS-mutePhrase'] = {
        'margin': '0px 4px',
    }

    rules['#AO3-MS-settings'] = {
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
        'background-color': darkmode? '#1f1f1f' : '#f1f1f1',
        'display': 'none',
        'flex-flow': 'column',
        'position': 'fixed',
        'top': '50%',
        'left': '50%',
        'width': fontSizing.menuWidth + 'px',
        'height': fontSizing.menuHeight + 'px',
        'margin': '-' + (fontSizing.menuHeight / 2 ) + 'px -' + (fontSizing.menuWidth / 2) + 'px',
        'box-shadow': '10px 10px 20px rgba(0, 0, 0, 0.5)',
        'z-index': '100',
        'border': '1px solid ' + (darkmode? '#f1f1f1' : '#1f1f1f') + '!important',
        'font-size': fontSizing.actionFont + 'px',
    };

    rules['#AO3-MS-settings a'] = {
        'color': darkmode? '#aaa' : '#666',
    };

    rules['#AO3-MS-settings a:hover'] = {
        'color': darkmode? '#ddd' : '#333',
    };

    rules['#AO3-MS-title'] = {
        'margin': '10px 0px 0px',
        'text-align': 'center',
        'font-size': fontSizing.titleFont + 'px',
    };

    rules['#AO3-MS-closeMenu'] = {
        'position': 'absolute',
        'top': '0px',
        'right': '0px',
        'margin': '5px',
        'width': (fontSizing.actionFont + 10) + 'px',
        'height': (fontSizing.actionFont + 10) + 'px',
        'font-size': fontSizing.actionFont + 'px',
    };

    rules['#AO3-MS-tablist'] = {
        'margin': '10px 0px',
        'text-align': 'center',
        'border-bottom': '1px solid ' + (darkmode? '#f1f1f1' : '#1f1f1f'),
    };

    rules['.AO3-MS-tab'] = {
        'display': 'inline-block',
        'padding': '5px',
        'height': fontSizing.tabFont + 'px',
        'background-color': darkmode? '#2f2f2f' : '#eee',
        'border': '1px solid ' + (darkmode? '#e2e2e2' : '#2e2e2e') + '!important',
        'box-size': 'border-box',
    };

    rules['#AO3-MS-viewport'] = {
        'display': 'flex',
        'flex-flow': 'column',
        'flex': '1 1 auto',
        'bottom': '0px',
    };

    rules['.AO3-MS-view'] = {
        'display': 'none',
    };

    rules['.AO3-MS-active.AO3-MS-view'] = {
        'display': 'flex',
        'flex-flow': 'column',
        'height': '100%',
        'bottom': '0px',
    };

    rules['.AO3-MS-active.AO3-MS-tab'] = {
        'background': darkmode? '#555' : '#ccc',
        'color': (darkmode? '#ddd' : '#333') + '!important',
    };

    rules['.AO3-MS-header'] = {
        'position': 'inline-block',
        'left': '0px',
        'right': '0px',
        'top': '0px',
        'margin': '5px 10px',
        'font-size': fontSizing.headerFont + 'px',
        'text-align': 'center',
    };

    rules['.AO3-MS-sortbar'] = {
        'position': 'inline-block',
        'left': '0px',
        'right': '0px',
        'top': '0px',
        'margin': '5px 10px',
        'text-align': 'center',
    };


    rules['.AO3-MS-sortbar a'] = {
        'margin': '0px 5px',
        'text-decoration': 'none',
    };

    rules['.AO3-MS-list'] = {
        'flex': '1 1 auto',
        'height': '100px',
        'overflow-y': 'scroll',
    };

    rules['.AO3-MS-refresh'] = {
        'position': 'inline-block',
        'font-size': fontSizing.refreshFont + 'px',
        'margin': '0px 10px',
        'text-align': 'center',
    };

    rules['#AO3-MS-checkboxes'] = {
        'flex': '1 1 auto',
        'height': '100px',
        'padding': '10px',
        'overflow-y': 'scroll',
    };

    rules['.AO3-MS-padding'] = {
        'display': 'flex',
        'flex-flow': 'column',
        'flex': '1 1 auto',
        'padding': '10px',
    };

    rules['.AO3-MS-textarea, .AO3-MS-textarea:focus'] = {
        'position': 'inline-block',
        'left': '0px',
        'width': '100%',
        'display': 'flex',
        'flex-flow': 'column',
        'padding': '10px',
        'height': '100%',
        'resize': 'none',
        'box-sizing': 'border-box',
        'background-color': darkmode? '#3e3e3e' : '#e3e3e3',
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
    };

    rules['#AO3-MS-viewport input[type="button"]'] = {
        'display': 'inline-block',
        'flex': '0 1 auto',
        'bottom': '0px',
        'margin': '5px 0px 0px 0px',
        'font-size': fontSizing.refreshFont + 'px',
        'box-shadow': '2px 1px 2px gray',
        'background-color': darkmode? '#3e3e3e' : '#e3e3e3',
        'color': darkmode? '#f1f1f1' : '#1f1f1f',
    };

    rules['#AO3-MS-viewport input[type="button"]:active'] = {
        'box-shadow': '0 0 0 ' + darkmode? '#333' : '#eee',
    };

    rules['#AO3-MS-css-reset,#AO3-MS-css-save'] = {
        'width': '100%',
    };

    rules['#AO3-MS-import-button'] = {
        'width': '100%',
    };

    rules['.AO3-MS-action'] = {
        'font-size': fontSizing.actionFont + 'px',
        'margin-left': '5px',
        'color': darkmode? '#7f7feb' : '#4d4dff !important',
    };

    rules['.AO3-MS-action:hover'] = {
        'font-size': fontSizing.actionFont + 'px',
        'margin-left': '5px',
        'color': darkmode? '#a5a5fa' : '#27279c !important',
    };

    rules['.AO3-MS-action:before'] = {
        'content': '"("',
    };

    rules['.AO3-MS-action:after'] = {
        'content': '")"',
    };

    rules['.AO3-MS-item'] = {
        'padding': '10px',
    };

    rules['.AO3-MS-item a'] = {
        'border': darkmode? '#333' : '#eee',
    };

    rules['.AO3-MS-filter:before'] = {
        'content': '\'"\'',
    };

    rules['.AO3-MS-filter:after'] = {
        'content': '\'"\'',
    };

    return rules;
}

function addMuteRules(muteByStyle) {

    var rules = {};

    rules[muteByStyle] = {
        'display': 'none !important',
    };

    return rules;
}

function makeStyleCode(rules) {
    var contents = "";

    for (var descriptor in rules) {
        if (! rules.hasOwnProperty(descriptor)) continue;

        contents += descriptor + " {\n";

        for (var property in rules[descriptor]) {
            if (! rules[descriptor].hasOwnProperty(property)) continue;

            contents += "    " + property + ": " + rules[descriptor][property] + ";\n";
        }

        contents += "}\n";
    }

    return contents;
}

function makeStyleSheet(str,identifier) {
    if (document.querySelectorAll('#' + identifier).length) {
        document.querySelector('#' + identifier).remove()
    }

    var sheet = document.createElement('style');
    sheet.innerHTML = str;
    sheet.id = identifier
    document.body.appendChild(sheet);
}

async function changeReason(identifier,index,reason) {
    let temp = await GM.getValue(identifier)
    temp[index][3] = reason

    await GM.setValue(identifier,temp)

    let affectedReasons = document.querySelectorAll('.AO3-MS-collapsed-' + temp[index][0] + ' .AO3-MS-mutePhraseReason')
    for (const r of affectedReasons) {
        if (!reason) {
            r.innerText = ''
        } else {
            r.innerText = ' (' + reason + ')'
        }
    }

    let refresh = refreshFilterList(identifier)
    refresh();
}

function makeBlurbFilterItem(identifier,index,value,refresh) {
    let element = document.createElement("div");
    element.className = "AO3-MS-item";
    let filter = document.createElement("span");
    filter.className = "AO3-MS-filter";

    let aTitle = document.createElement('a');
    aTitle.href = 'https://archiveofourown.org/works/' + value[0].split('work-')[1]
    aTitle.appendChild(document.createTextNode(value[1]));
    element.appendChild(aTitle)
    element.appendChild(document.createTextNode(' by '))

    let names = value[2].split(', ')
    for (let i = 0; i < names.length; i++) {
        let aName = document.createElement('a')
        let linkName = names[i].includes(' (')? names[i].split(' (')[1].split(')')[0] : names[i]
        aName.href = 'https://archiveofourown.org/users/' + linkName
        aName.appendChild(document.createTextNode(names[i]))
        element.appendChild(aName)
        if (i < names.length - 1) {
            element.appendChild(document.createTextNode(', '))
        }
    }

    let remove = makeActionLink("unmute", async function() {
        await delVal(identifier,value[0])
        await refresh();
    });
    element.appendChild(remove)

    let reason = document.createTextNode(' Reason: ' + value[3])
    element.appendChild(reason)

    let editReason = makeActionLink('edit reason', async function () {
        let newReason = prompt("Reason for muting " + value[1] + ":", value[3]);
        if (newReason === null) {
            return null;
        }
        await changeReason(identifier,index,newReason)
        await refresh();
    });
    element.appendChild(editReason)

    return element;
}

function makeUserFilterItem(identifier, index, value, refresh) {
    let element = document.createElement("div");
    element.className = "AO3-MS-item";
    let filter = document.createElement("span");
    filter.className = "AO3-MS-filter";

    let id = document.createTextNode('(UserID ' + value[0].split('user-')[1] + ') ')

    let aName = document.createElement('a');
    aName.href = 'https://archiveofourown.org/users/' + value[2]
    aName.appendChild(document.createTextNode(value[2]));

    let remove = document.createElement('a')
    remove.className = "AO3-MS-action";
    remove.href = "javascript:void(0)";
    remove.appendChild(document.createTextNode('edit'));
    remove.onclick = async function() {
        await toggleUser(element,remove,value[0],value[2])
    };

    let reason = document.createTextNode(' Reason: ' + value[3])

    let editReason = makeActionLink('edit reason', async function () {
        let newReason = prompt("Reason for muting " + value[2] + ":", value[3]);
        if (newReason === null) {
            return null;
        }
        await changeReason(identifier,index,newReason)
        await refresh();
    });

    element.appendChild(id)
    element.appendChild(aName)
    element.appendChild(remove)
    element.appendChild(reason)
    element.appendChild(editReason)

    return element;
}

function makeTagFilterItem(identifier,index,value,refresh) {
    let element = document.createElement("div");
    element.className = "AO3-MS-item";
    let filter = document.createElement("span");
    filter.className = "AO3-MS-filter";

    let tagName = document.createElement('a');
    tagName.href = value[1]
    tagName.appendChild(document.createTextNode(value[2]));
    element.appendChild(tagName)

    let remove = makeActionLink("unmute", async function() {
        await delVal(identifier,value[0])
        await refresh();
    });
    element.appendChild(remove)

    let reason = document.createTextNode(' Reason: ' + value[3])
    element.appendChild(reason)

    let editReason = makeActionLink('edit reason', async function () {
        let newReason = prompt("Reason for muting " + value[2] + ":", value[3]);
        if (newReason === null) {
            return null;
        }
        await changeReason(identifier,index,newReason)
        await refresh();
    });
    element.appendChild(editReason)

    return element;
}

function refreshFilterList(identifier) {
    let refresh = async function() {
        // Load the array first to prevent visual glitches.
        let array = await GM.getValue(identifier,[]),
            list = document.querySelector('#AO3-MS-list-' + identifier)

        while (list.firstChild !== null) {
            list.removeChild(list.firstChild);
        }

        if (identifier === 'blurblist') {
            for (let i = 0; i < array.length; ++i) {
                list.appendChild(makeBlurbFilterItem(identifier, i, array[i], refresh));
            }
        } else if (identifier === 'userlist') {
            for (let i = 0; i < array.length; ++i) {
                list.appendChild(makeUserFilterItem(identifier, i, array[i], refresh));
            }
        } else if (identifier === 'taglist') {
            for (let i = 0; i < array.length; ++i) {
                list.appendChild(makeTagFilterItem(identifier, i, array[i], refresh));
            }
        }

    };

    return refresh;
}

async function sortList(listType, sortBy, reverse) {

    let list = await GM.getValue(listType,[]);
    if (!list.length) return;

    let i = 2; // sortBy === 'Name'
    if (sortBy === 'Title') i = 1;
    if (sortBy === 'Date') i = 4;

    if (sortBy === 'id') {
        list.sort(function(a, b) {
            let x = a[0].split('-')[1]*1;
            let y = b[0].split('-')[1]*1;
            return x - y;
        });
    } else if (sortBy === 'Date') {
        list.sort(function(a, b) {
            let x = (a[i]? a[i]: 0);
            let y = (b[i]? b[i]: 0);
            return x - y;
        });
    }else {
        list.sort(function(a, b) {
            let x = a[i].toLowerCase();
            let y = b[i].toLowerCase();
            if (x < y) return -1;
            if (y < x) return 1;
            return 0;
        });
    }
    if (reverse) list.reverse();

    await GM.setValue(listType, list);
    let refresh = refreshFilterList(listType);
    refresh();
}

function makeSortButton(container, listType, title) {
    let a = document.createElement('a');
    a.href = "javascript:void(0)";
    a.appendChild(document.createTextNode(title + '↕'));

    a.onclick = function() {
        let reverse = a.innerText.includes('↓')
        sortList(listType, title, reverse)
        a.innerText = a.innerText.slice(0, -1) + (reverse? '↑' : '↓')
    }

    container.appendChild(a)
}

function makeFilterList(container, identifier, title) {
    let header = document.createElement("div");
    header.className = "AO3-MS-header";
    header.appendChild(document.createTextNode(title));

    let sortBar = document.createElement("div");
    sortBar.className = "AO3-MS-sortbar";

    if (identifier === 'blurblist') {
        makeSortButton(sortBar, identifier, "Title")
        makeSortButton(sortBar, identifier, "Author")
    } else {
        makeSortButton(sortBar, identifier, "Name")
    }
    makeSortButton(sortBar, identifier, "Date")

    let list = document.createElement("div");
    list.id = "AO3-MS-list-" + identifier;
    list.className = "AO3-MS-list";

    let refresh = refreshFilterList(identifier)
    refresh();

    container.appendChild(header);
    container.appendChild(sortBar);
    container.appendChild(list);

    return refresh;
}

function formatListNameForId(val) {
    val = String(val).charAt(0).toUpperCase() + String(val).slice(1)
    return val.split('list')[0] + 's'
}

function makeMutingTab(viewport, listType, header) {

    // Create the tab showing blocked entries
    let mutelist = document.createElement('div');
    mutelist.id = 'AO3-MS-muted' + formatListNameForId(listType);
    viewport.appendChild(mutelist);

    let listRefresh = makeFilterList(mutelist, listType, header);

    return async function() {
        await listRefresh();
    };
}

async function addCheckbox(container, identifier, caption, hover) {
    var item = document.createElement("div");

    var checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.id = "AO3-MS-settings-" + identifier;

    checkbox.checked = await GM.getValue(identifier, false);
    checkbox.onclick = async function() {
        await GM.setValue(identifier, checkbox.checked);
    };

    var label = document.createElement("label");
    label.appendChild(document.createTextNode(caption));
    label.setAttribute('for', "AO3-MS-settings-" + identifier);
    label.setAttribute('title', hover);

    item.appendChild(checkbox);
    item.appendChild(label);
    container.appendChild(item);

    var refresh = async function() {
        checkbox.checked = await GM.getValue(identifier, false);
    };

    return refresh;
}

async function makeFilterTab(viewport) {
    // Create the tab with the general settings.
    var general = document.createElement("div");
    general.id = "AO3-MS-general";
    viewport.appendChild(general);

    var header = document.createElement("div");
    header.className = "AO3-MS-header";
    header.appendChild(document.createTextNode("Filter Settings"));
    general.appendChild(header);

    var refresh = document.createElement("div");
    refresh.className = "AO3-MS-refresh";
    refresh.appendChild(document.createTextNode("Refresh the page to make new extension settings take effect."));
    general.appendChild(refresh);

    var checkboxes = document.createElement("div");
    checkboxes.id = "AO3-MS-checkboxes";
    general.appendChild(checkboxes);

    var refreshList = [];

    refreshList.push(
        await addCheckbox(checkboxes, "mute",
                          "Remove 'Hidden content' messages (aka total mute).",
                          "When unchecked, the content vanishes without trace for works and series.\n"+
                          "When checked, it will display a message to indicate a work/series has been hidden.\n"+
                          "Note that this means you'll only be able to unmute items from the menu.\n"+
                          "Bookmarks and comments by a muted user will not have hidden content indicators."));
    refreshList.push(
        await addCheckbox(checkboxes, "addReason",
                          "Ask me for a reason whenever something/someone is muted.",
                          "When unchecked, it will mute in one click and leave the reason blank.\n" +
                          "When checked, it will prompt for a reason before the mute goes through."));
    refreshList.push(
        await addCheckbox(checkboxes, "showName",
                          "Show name/title in hidden content message for works and series.",
                          "If there is a hidden content message, it defaults to a generic 'Hidden content' message.\n"+
                          "If you check this box, then it will display the name of the user who posted the content.\n"+
                          "If the specific work/series was muted, it will also display the title."));
    refreshList.push(
        await addCheckbox(checkboxes, "showReason",
                          "Show reason for muting in hidden content message for works and series.",
                          "If there is a hidden content message, it defaults to a generic 'Hidden content' message.\n"+
                          "If this box is checked, it will display the reason you entered for muting the content."));
    refreshList.push(
        await addCheckbox(checkboxes, "showTagToggles",
                          "Show tag toggles for all blurbs in works listings.",
                          "If left unchecked, will only show toggles on individual works and page headings. \n"+
                          "May not be recommended for older/slower mobile devices.\n"+
                          "By default, tag toggles will show at the header of every tag page and tag work listing, and on individual works."));
    refreshList.push(
        await addCheckbox(checkboxes, "forceMobile",
                          "Display in mobile-friendly view even on larger screens.",
                          "Displays a smaller window with smaller font."));

    refreshList.push(
        await addCheckbox(checkboxes, "darkmode",
                          "Use darkmode theme.",
                          "You can also use the CSS tab to set up a custom style."));

    return async function() {
        for (var i = 0; i < refreshList.length; ++i) {
            await refreshList[i]();
        }
    };
}

// Unicode fix from https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
function utf8_to_b64(str) {
    return window.btoa(unescape(encodeURIComponent(str)));
}

function b64_to_utf8(str) {
    return decodeURIComponent(escape(window.atob(str)));
}

async function getFilterSettings() {
    var settings = {};

    settings.userlist = await GM.getValue('userlist');
    settings.blurblist = await GM.getValue('blurblist');
    settings.taglist = await GM.getValue('taglist');

    var booleanNames = [
        'addReason',
        'mute',
        'showName',
        'showReason',
        'forceMobile',
        'showTagToggles',
        'darkmode',
    ];

    for (var i = 0; i < booleanNames.length; ++i) {
        var name = booleanNames[i];
        settings[name] = await GM.getValue(name, false);
    }

    settings['custom-css'] = await loadCustomCSS();

    return settings;
}

async function saveFilterSettings(settings) {

    await GM.setValue('userlist',settings.userlist)
    await GM.setValue('blurblist',settings.blurblist)

    var booleanNames = [
        'addReason',
        'mute',
        'showName',
        'showReason',
        'forceMobile',
        'showTagToggles',
        'darkmode',
    ];

    for (let i = 0; i < booleanNames.length; ++i) {
        var name = booleanNames[i];
        if (settings.hasOwnProperty(name)) {
            GM.setValue(name, settings[name]);
        }
    }

    if (settings.hasOwnProperty('custom-css')) {
        GM.setValue('custom-css', settings['custom-css']);
    }
}



async function updateExport() {
    var exportText = document.getElementById("AO3-MS-export");
    var settings = await getFilterSettings();

    exportText.value = utf8_to_b64(JSON.stringify(settings));
    exportText.select();
    exportText.focus();
}

async function doImport() {
    var importText = document.getElementById("AO3-MS-import");

    try {
        var settings = JSON.parse(b64_to_utf8(importText.value));
        await saveFilterSettings(settings);
        updateExport();
    } catch (e) {
        alert("Invalid data.");
    }
}

function makeImportTab(viewport) {
    // Make tab to import settings & filters
    var contMain = document.createElement("div");
    contMain.id = "AO3-MS-importMain";
    viewport.appendChild(contMain);

    var header = document.createElement("div");
    header.className = "AO3-MS-header";
    header.appendChild(document.createTextNode("Import settings & filters"));
    contMain.appendChild(header);

    var refresh = document.createElement("div");
    refresh.className = "AO3-MS-refresh";
    refresh.appendChild(document.createTextNode("Refresh the page to make new extension settings take effect."));
    contMain.appendChild(refresh);

    var padder = document.createElement("div")
    padder.className = "AO3-MS-padding"
    contMain.appendChild(padder)

    var importText = document.createElement("textarea");
    importText.id = "AO3-MS-import";
    importText.className = "AO3-MS-textarea";
    padder.appendChild(importText);

    var importButton = document.createElement("input");
    importButton.id = "AO3-MS-import-button";
    importButton.type = "button";
    importButton.value = "Import";
    importButton.onclick = doImport;
    padder.appendChild(importButton);

}

function makeExportTab(viewport) {

    // Create the tab with save and export boxes.
    var contMain = document.createElement("div");
    contMain.id = "AO3-MS-exportMain";
    viewport.appendChild(contMain);

    var exportHeader = document.createElement("div");
    exportHeader.className = "AO3-MS-header";
    exportHeader.appendChild(document.createTextNode("Export settings & filters"));
    contMain.appendChild(exportHeader);

    var padder = document.createElement("div")
    padder.className = "AO3-MS-padding"
    contMain.appendChild(padder)

    var exportText = document.createElement("textarea");
    exportText.id = "AO3-MS-export";
    exportText.className = "AO3-MS-textarea";
    exportText.readOnly = true;
    padder.appendChild(exportText);

    return updateExport;
}

function getDefaultRules() {
    var rules = {};

    var blockedBackground = 'none';
    var blockedColor = 'none';

    rules['.AO3-MS-collapseBlurb'] = {
        'background-color': blockedBackground,
        'color': blockedColor,
        'padding': '20px',
    }

    return rules;
}

async function loadCustomCSS() {
    var result = await GM.getValue('custom-css', null);
    if (result === null) {
        result = makeStyleCode(getDefaultRules());
    }
    return result;
}

async function updateCSS() {
    var cssText = document.getElementById("AO3-MS-css");
    cssText.value = await loadCustomCSS();
}

async function saveCSS() {
    var cssText = document.getElementById("AO3-MS-css");
    await GM.setValue('custom-css', cssText.value);
}

async function resetCSS() {
    await GM.deleteValue('custom-css');
    await updateCSS();
}

function makeColorsTab(viewport) {
    // Create the tab with the custom CSS editor.
    var colors = document.createElement("div");
    colors.id = "AO3-MS-colors";
    viewport.appendChild(colors);

    var cssHeader = document.createElement("div");
    cssHeader.className = "AO3-MS-header";
    cssHeader.appendChild(document.createTextNode("Custom styling"));
    colors.appendChild(cssHeader);

    var refresh = document.createElement("div");
    refresh.className = "AO3-MS-refresh";
    refresh.appendChild(document.createTextNode("Refresh the page to make new extension settings take effect."));
    colors.appendChild(refresh);

    var padder = document.createElement("div")
    padder.className = "AO3-MS-padding"
    colors.appendChild(padder)

    var css = document.createElement("textarea");
    css.id = "AO3-MS-css";
    css.className = "AO3-MS-textarea";
    padder.appendChild(css);

    var cssSave = document.createElement("input");
    cssSave.id = "AO3-MS-css-save";
    cssSave.className = "AO3-MS-css-button";
    cssSave.type = "button";
    cssSave.value = "Save";
    cssSave.onclick = async function () {
        await GM.setValue('custom-css',css.value)
    };
    padder.appendChild(cssSave);

    var cssReset = document.createElement("input");
    cssReset.id = "AO3-MS-css-reset";
    cssReset.className = "AO3-MS-css-button";
    cssReset.type = "button";
    cssReset.value = "Reset to Default";
    cssReset.onclick = resetCSS;
    padder.appendChild(cssReset);

    css.onchange = saveCSS;

    return updateCSS;
}

function toggleSettings() {
    var settings = document.getElementById("AO3-MS-settings");
    if (settings === null) return;

    if (settings.style.display == "flex") {
        settings.style.top = "-50%";
        settings.style.display = "none";
    } else {
        settings.style.display = "flex";
        settings.style.top = "50%";
    }
}

function addTab(container, name, view, active, action) {
    var classView = "AO3-MS-view";
    var classActive = "AO3-MS-active";
    var classTab = "AO3-MS-tab";

    var link = document.createElement("a");
    link.className = classTab;
    link.href = "javascript:void(0)";
    var text = document.createTextNode(name);
    link.appendChild(text);
    container.appendChild(link);

    var viewElement = document.getElementById(view);
    viewElement.className = classView;

    link.onclick = function(e) {
        var views = document.getElementsByClassName(classView);
        for (let i = 0; i < views.length; ++i) {
            views[i].className = classView;
        }

        var tabs = document.getElementsByClassName(classTab);
        for (let i = 0; i < tabs.length; ++i) {
            tabs[i].className = classTab;
        }

        viewElement.className += " " + classActive;
        link.className += " " + classActive;

        if (action !== undefined && action !== null) {
            action();
        }
    };

    if (active) {
        viewElement.className += " " + classActive;
        link.className += " " + classActive;
    }
}

function makeActionLink(text, action) {
    var link = document.createElement("a");
    link.href = "javascript:void(0)";
    link.className = "AO3-MS-action";
    link.onclick = action;
    link.appendChild(document.createTextNode(text));
    return link;
}


async function makeSettings() {
    // Create the settings window.
    var settings = document.createElement("div");
    settings.id = "AO3-MS-settings";

    var header = document.createElement("div");
    header.id = "AO3-MS-title";
    header.appendChild(document.createTextNode("Selective Muting"));
    settings.appendChild(header);

    var closeButton = document.createElement("input");
    closeButton.id = "AO3-MS-closeMenu";
    closeButton.type = "button";
    closeButton.value = "X";
    closeButton.onclick = function () {
        toggleSettings()
    };
    header.appendChild(closeButton);

    var tabs = document.createElement("div");
    tabs.id = "AO3-MS-tablist";
    settings.appendChild(tabs);

    var viewport = document.createElement("div");
    viewport.id = "AO3-MS-viewport";
    settings.appendChild(viewport);

    document.body.appendChild(settings);

    // Create the tabs and add them.
    var blurblistRefresh = await makeMutingTab(viewport, 'blurblist', 'Works & Series');
    var userlistRefresh = await makeMutingTab(viewport, 'userlist', 'Users');
    var taglistRefresh = await makeMutingTab(viewport, 'taglist', 'Tags');
    var generalRefresh = await makeFilterTab(viewport);
    var colorsRefresh = await makeColorsTab(viewport);
    var importRefresh = await makeImportTab(viewport);
    var exportRefresh = await makeExportTab(viewport);

    addTab(tabs, "Works/Series", "AO3-MS-mutedBlurbs", true, blurblistRefresh);
    addTab(tabs, "Users", "AO3-MS-mutedUsers", false, userlistRefresh);
    addTab(tabs, "Tags", "AO3-MS-mutedTags", false, taglistRefresh);
    addTab(tabs, "Settings", "AO3-MS-general", false, generalRefresh);
    addTab(tabs, "CSS", "AO3-MS-colors", false, colorsRefresh);
    addTab(tabs, "Import", "AO3-MS-importMain", false, importRefresh);
    addTab(tabs, "Export", "AO3-MS-exportMain", false, exportRefresh);
}

async function makeCollapseBlurb(blurb,blurbInfo) {
    let container = document.createElement('li')
    container.classList.add('AO3-MS-collapseBlurb')
    container.classList.add('blurb')
    container.classList.add('AO3-MS-collapsed-' + blurbInfo[0])
    container.classList.add(blurb.id)
    blurb.parentElement.insertBefore(container,blurb)

    await addToCollapseBlurb(container, blurbInfo, blurb)
}

async function addToCollapseBlurb(container, blurbInfo, blurb) {
    let mutePhrase = document.createElement('span')
    mutePhrase.classList.add('AO3-MS-mutePhrase')
    mutePhrase.innerText = 'Hidden content'

    let reason = document.createElement('span')
    reason.classList.add('AO3-MS-mutePhraseReason')

    if (blurbInfo[1].split('/tags/').length > 1) {
        if (blurb) {
            makeTogglebox(container, '(show)', container) // Add a * or something in front to fast unmute? Ugh
        }
        if (container.innerText.includes('because of tag:')) {
            mutePhrase.innerHTML = ', <a href="' + blurbInfo[1] + '">' + blurbInfo[2] + '</a>'
        } else if (await GM.getValue('showName')) {
            mutePhrase.innerHTML += ' because of tag: <a href="' + blurbInfo[1] + '">' + blurbInfo[2] + '</a>'
        }
        if (await GM.getValue('showReason') && blurbInfo[3]) {
            reason.innerText += ' (' + blurbInfo[3] + ')'
        }
        container.appendChild(mutePhrase)
        container.appendChild(reason)
    } else if (blurbInfo[0].split('work').length > 1 || blurbInfo[0].split('series').length > 1) {
        if (blurb) makeTogglebox(blurb,'m',container)
        if (await GM.getValue('showName')) {
            mutePhrase.innerText += ': ' + blurbInfo[1] + ' by ' + blurbInfo[2]
        }
        if (await GM.getValue('showReason') && blurbInfo[3]) {
            reason.innerText += ' (' + blurbInfo[3] + ')'
        }
        container.appendChild(mutePhrase)
        container.appendChild(reason)
    } else if (!blurb?.classList?.contains('bookmark')) {
        if (await GM.getValue('showName')) {
            mutePhrase.innerText += ' by ' + blurbInfo[2]
        }
        if (await GM.getValue('showReason') && blurbInfo[3]) {
            reason.innerText += ' (' + blurbInfo[3] + ')'
        }
        container.appendChild(mutePhrase)
        container.appendChild(reason)
        if (blurb) makeTogglebox(blurb,'X',container)
    }
}

function getBlurbs(arrayName,value) {
    let blurbuser = arrayName.split('list')[0],
        toHide = ''

    if (blurbuser === 'blurb') {
        toHide = '.' + value[0]
    } else if (blurbuser === 'user') {
        value[1].split(',').forEach(w => {
            toHide += ',' + w + '.' + value[0]
        })
        toHide = toHide.substring(1)
    } else if (blurbuser === 'tag') {
        toHide = '.blurb:has(a[href$="' + value[1] + '"])'
    }

    if (!toHide) return null

    return document.querySelectorAll(toHide)
}

async function addVal(arrayName,value) {
    // Add the entry to the saved array
    let temp = await GM.getValue(arrayName,[])
    for (let i = 0; i < temp.length; ++i) {
        if (temp[i][0] === value[0]) {
            return null;
        }
    }

    let reason = ''
    if (value[3] == 'undefined' && await GM.getValue('addReason',false)) {
        let muteObj = value[1]
        if (arrayName === 'userlist') {
            muteObj = value[2]
        }
        reason = prompt("Reason for muting " + muteObj + ":", '');

        if (reason === null) {
            return '';
        }
    }
    if (value[3] == undefined) value.push(reason);

    value.push(Date.now());
    temp.push(value);
    await GM.setValue(arrayName,temp)

    // Collapse/mute all works on the page that meet the criteria
    let mute = await GM.getValue('mute', false)
    let blurbs = []

    if (arrayName === 'taglist') {
        blurbs = document.querySelectorAll('.blurb:has(a[href$="' + value[1] + '"])')
    } else {
        blurbs = getBlurbs(arrayName,value)
    }

    for (let blurb of blurbs) {
        if (!mute &&
            (arrayName === 'blurblist' || blurb.classList.contains('work') || blurb.classList.contains('series')) ) {
            const collapseBlurb = document.querySelector('.' + blurb.id)
            if (!collapseBlurb) {
                await makeCollapseBlurb(blurb, value)
            } else {
                await addToCollapseBlurb(collapseBlurb, value)
            }
        }
        blurb.classList.add('AO3-MS-muted')
    }

    let refresh = refreshFilterList(arrayName)
    refresh()

    return true
}

async function delVal(arrayName,key) {
    // Remove the entry from the saved array
    let temp = await GM.getValue(arrayName,false)
    let value = [];

    let i = 0;
    while (i < temp.length) {
        if (temp[i][0] === key) {
            value = temp[i];
            temp.splice(i, 1);
        } else {
            ++i;
        }
    }
    await GM.setValue(arrayName,temp)

    // Uncollapse/unmute all works on the page that meet the criteria
    let mute = await GM.getValue('mute',false)

    let blurbs = getBlurbs(arrayName,value)

    if (!mute) {
        for (let c of document.querySelectorAll('.AO3-MS-collapsed-' + key)) {
            c.remove();
        }
    }

    for (let blurb of blurbs) {
        blurb.classList.remove('AO3-MS-muted')
    }

    if(arrayName === 'userlist') {
        let delMe = []

        for (let i = 0; i < commentMute.length; i++) {
            if (commentMute[i].includes(value[0])) {
                let delMe = commentMute.splice(i,1)
                delMe ? makeStyleSheet(makeStyleCode(addMuteRules(commentMute.join(','))),'AO3-MS-muteByStyle') : null
            }
        }
        setInitialMute('blurblist')
    }

    let refresh = refreshFilterList(arrayName)
    refresh()
}

async function getVal(arrayName, key, localArray) {
    let temp = []
    if (localArray) {
        temp = arrayName
    } else {
        temp = await GM.getValue(arrayName, []);
    }
    if (!temp) return null;

    for (let i = 0; i < temp.length; ++i) {
        if (temp[i][0] === key) {
            return temp[i]
        }
    }
    return null;
}

function getBlurbUserID(blurb) {
    for (let e of blurb.classList) {
        if (e.includes('user-')) {
            return e;
        }
    }
    if (document.querySelector('#main.dashboard') && document.querySelector('#subscription_subscribable_type')?.value === 'User') {
        return document.querySelector('#subscription_subscribable_id')?.value
    }
    return null;
}

async function toggleBlurb(blurb,toggleboxA) {

    let blurbInfo = [];
    if (blurb) {
        for (let e of blurb.classList) {
            if (e.includes('work-') || e.includes('series-')) {
                blurbInfo.push(e)
            }
        }
        // record title
        blurbInfo.push(blurb.querySelectorAll('.heading a')[1].textContent)

        // record creators
        if (!blurb.querySelectorAll('h4.heading [rel="author"]').length) {
            blurbInfo.push('(Anonymous creator)')
        } else {
            let authorNames = ''
            blurb.querySelectorAll('h4.heading [rel="author"]').forEach (a => {
                authorNames += ', ' + a.innerText
            })
            authorNames = authorNames.substring(2)
            blurbInfo.push(authorNames)
        }

    } else if (document.querySelectorAll('h2.title.heading').length) {
        blurbInfo.push(getIdFromWorkPage())
        blurbInfo.push(document.querySelector('h2.title.heading').textContent.substring(1).trim())
        if (!document.querySelectorAll('a[rel="author"]').length) {
            blurbInfo.push('(Anonymous creator)')
        } else {
            blurbInfo.push(document.querySelector('a[rel="author"]').innerText)
        }
    } else if (document.querySelectorAll('.series-show h2.heading').length) {
        blurbInfo.push(getIdFromSeriesPage())
        blurbInfo.push(document.querySelector('.series-show h2.heading').textContent.substring(1).trim())
        if (!document.querySelectorAll('a[rel="author"]').length) {
            blurbInfo.push('(Anonymous creator)')
        } else {
            blurbInfo.push(document.querySelector('a[rel="author"]').innerText)
        }
    }

    if(toggleboxA.textContent == 'M') {
        await addVal('blurblist',blurbInfo) && !blurb ? toggleboxA.textContent = 'm' : ''
        // The M->m change only actually matters on title pages; on blurblists M vs m are actually in two different blurbs
    } else {
        await delVal('blurblist',blurbInfo[0])
        !blurb ? toggleboxA.textContent = 'M' : ''
    }
}

async function toggleUser(blurb,togglebox,userID,username) {

    if (togglebox.parentNode.querySelectorAll('.AO3-MS-usermenu').length){
        togglebox.parentNode.querySelector('.AO3-MS-usermenu').remove()
        return null;
    }

    let usermenu = document.createElement('div');
    usermenu.id = 'AO3-MS-usermenu-' + userID;
    usermenu.classList.add('AO3-MS-usermenu')

    if(!blurb) {
        // Prevent the menu from being enormous on profile pages
        usermenu.style.fontSize = fontSizing.refreshFont + 'px'
        usermenu.style.display = 'block'
    }

    let items = [['.work','Works'],
                 ['.work,.series','Works & Series'],
                 ['.comment','Comments'],
                 ['','Mute all'],
                 ['none','Unmute']]

    const currEntry = await getVal('userlist',userID)
    let reason = ''

    async function createItem(item) {
        let container = document.createElement('span')
        container.classList.add('AO3-MS-usermenu-item')
        usermenu.appendChild(container)

        // check what the current option is, and highlight it
        if (currEntry) {
            if (currEntry[1] === item[0]) {
                container.classList.add('AO3-MS-usermenu-item-selected')
            }
        } else if (item[0] === 'none') {
            container.classList.add('AO3-MS-usermenu-item-selected')
        }

        let a = document.createElement('a')
        a.href = "javascript:void(0)";
        a.appendChild(document.createTextNode(item[1]));
        container.appendChild(a)

        a.onclick = async function() {
            if (currEntry) {
                if (item[0] != currEntry[1]) {
                    reason = currEntry[3]
                    await delVal('userlist',userID)
                }
            }
            if (item[0] != 'none') {
                let newEntry = [userID,item[0],username,reason];
                await addVal('userlist',newEntry);
            }

            // remove all menues on the page because things have probably changed
            let rl = document.querySelectorAll('.AO3-MS-usermenu')
            for (let ri of rl) {
                ri.remove()
            }
        }
        item.push(container)
    }

    for (let i = 0; i < items.length; i++) {
        await createItem(items[i])
    }

    togglebox.parentElement.appendChild(usermenu)

    // Fix if it's likely to get squished -- not great, but eh
    if (blurb && (await GM.getValue('forceMobile',false) || window.innerWidth < 900)) {
        usermenu.style.display = 'block'
    }
}

function makeTogglebox(blurb, tType, pin) {
    let togglebox = document.createElement('div');
    let toggleboxA = document.createElement('a');
    togglebox.classList.add('AO3-MS-toggle');
    togglebox.appendChild(toggleboxA);

    toggleboxA.href = "javascript:void(0)";
    toggleboxA.appendChild(document.createTextNode(tType));

    if (blurb) {
        if (tType == 'X') {
            toggleboxA.onclick = async function() {
                let username = blurb.querySelector('.heading a[rel="author"]').textContent
                await toggleUser(blurb,togglebox,getBlurbUserID(blurb),username)
            };
        } else if (tType == '(show)') {
            if (blurb.className.includes(' work_') || blurb.className.includes(' series_')) {
                toggleboxA.onclick = function () {
                    let originalBlurbId = blurb.className.split(' work_')
                    if (originalBlurbId.length > 1) {
                        originalBlurbId = 'work_' + originalBlurbId[1].split(' ')[0]
                    } else {
                        originalBlurbId = 'series_' + blurb.className.split(' series_')[1].split(' ')[0]
                    }
                    let originalBlurb = document.querySelector('#' + originalBlurbId)
                    originalBlurb.classList.remove('AO3-MS-muted')
                    blurb.remove()
                }
            }
        } else {
            toggleboxA.onclick = async function() {
                await toggleBlurb(blurb,toggleboxA)
            };
        }
    } else {
        if (tType == 'X') {
            toggleboxA.onclick = async function() {
                let username = document.querySelector('h2.heading').innerText.split(' ')[0]
                await toggleUser(false,togglebox,getIdFromProfilePage(),username)
            }
        } else {
            toggleboxA.onclick = async function() {
                await toggleBlurb(false,toggleboxA)
            };
        }
    }

    if (tType == 'X') {
        pin.append(togglebox)
    } else {
        pin.prepend(togglebox)
    }
}

function addMuteButtonToBlurbs() {
    const blurbs = document.querySelectorAll('.blurb.work,.blurb.series,.blurb.bookmark');

    for (let blurb of blurbs) {
        let blurbTitle = blurb.querySelector('.heading')

        makeTogglebox(blurb,'M',blurbTitle)

        if(blurb.querySelectorAll('h4.heading a').length == 3 && !blurb.classList.contains('bookmark')) {
            makeTogglebox(blurb,'X',blurbTitle)
        }
    }
}

async function setInitialMute(identifier) {
    let entries = await GM.getValue(identifier,[]);
    if (!entries) {
        return null;
    }

    let mute = await GM.getValue('mute',false),
        blurbs = ''

    for (const entry of entries) {
        if (identifier === 'userlist') {
            let typeToHide = ''
            if (entry[1].includes('work')) {
                entry[1].split(',').forEach(w => {
                    typeToHide += ',' + w + '.' + entry[0]
                })
            } else {
                typeToHide = ',.work.' + entry[0] + ',.series.' + entry[0]
            }
            typeToHide = typeToHide.substring(1)
            typeToHide ? blurbs = document.querySelectorAll(typeToHide) : null
        } else if (identifier === 'taglist') {
            blurbs = document.querySelectorAll('.blurb:has(a[href$="' + entry[1] + '"])')
        } else {
            blurbs = document.querySelectorAll('.' + entry[0])
        }

        for (let blurb of blurbs) {
            //if (!blurb.classList.contains('AO3-MS-muted')) {
                if (!mute) {
                    const collapseBlurb = document.querySelector('.' + blurb.id)
                    if (!collapseBlurb) {
                        await makeCollapseBlurb(blurb,entry)
                    } else {
                        await addToCollapseBlurb(collapseBlurb, entry)
                    }
                }
                blurb.classList.add('AO3-MS-muted')
            //}
        }
    }
}

function getIdFromWorkPage() {
    if (document.querySelectorAll('li.download a').length > 1) {
        if (isNaN(document.querySelectorAll('li.download a')[1].href.split('/')[4])) {
            return null;
        } else {
            return 'work-' + document.querySelectorAll('li.download a')[1].href.split('/')[4]
        }
    }
    return null;
}

function getIdFromSeriesPage() {
    let parts = window.location.pathname.split('/')
    for (let i = 0; i < parts.length - 1; i++) {
        if (parts[i] === 'series' && !isNaN(parts[i+1])) {
            return 'series-' + parts[i+1]
        }
    }
    return null
}

function getIdFromProfilePage() {
    if(document.querySelectorAll('.meta').length) {
        let parts = document.querySelector('.meta').innerText.split('\n')
        for (let i = 0; i < parts.length - 1; i++) {
            if (parts[i] === 'My user ID is:' && !isNaN(parts[i+1])) {
                return 'user-' + parts[i+1]
            }
        }
    } else if (document.querySelector('#main.dashboard') && document.querySelector('#subscription_subscribable_type')?.value === 'User') { // dashboard, logged in
        return 'user-' + document.querySelector('#subscription_subscribable_id')?.value
    }
    return null
}

async function toggleTag (tagInfo, toggleboxA) {
    let entries = await GM.getValue('taglist',[]);
    let blurbInfo = [];

    if (toggleboxA.innerText === "M") {
        toggleboxA.innerText = "m"
        await addVal('taglist', tagInfo)
    } else {
        toggleboxA.innerText = 'M'
        await delVal('taglist', tagInfo[0])
    }
}

function makeTagTogglebox(tType, pin, tagInfo) {
    let togglebox = document.createElement('div');
    let toggleboxA = document.createElement('a');
    togglebox.classList.add('AO3-MS-toggle');
    togglebox.appendChild(toggleboxA);

    toggleboxA.href = "javascript:void(0)";
    toggleboxA.appendChild(document.createTextNode(tType));

    toggleboxA.onclick = async function() {
        await toggleTag(tagInfo, toggleboxA)
    };

    pin.append(togglebox)
}

function makeTagKey(tagLink) {
    return tagLink.split('/')[2].replaceAll(/[^a-zA-Z0-9 :]/g, '_');
}

async function setTagMute() {
    let parts = window.location.pathname.split('/')
    let h2 = document.querySelector('h2')

    let tagInfo = []
    tagInfo.push('')

    if (parts[1] === 'tags' && parts[2]) {

        if (parts[3]) {
            // If you are on the tag's works or bookmarks listing
            if (await GM.getValue('showTagToggles', false)) return; // abort early, this will be taken care of in addTogglesToWorkTags()
            if (['works', 'bookmarks'].includes(parts[3])) {
                let a = h2?.querySelector('a')
                tagInfo.push(a.href.split('.org')[1] + '/works') // tagLink
                tagInfo.push(a.innerText) // tagName
            }
        } else {
            // If you're directly on the tag page
            tagInfo.push(window.location.pathname + '/works') // tagLink
            tagInfo.push(h2?.innerText) // tagName
        }
        tagInfo[0] = makeTagKey(tagInfo[1])
        let tType = 'M'
        if (await getVal('taglist', tagInfo[0])) tType = 'm'

        makeTagTogglebox(tType, h2, tagInfo)
    }
}

async function addTogglesToWorkTags() {
    const tags = document.querySelectorAll('a.tag')
    const taglist = await GM.getValue('taglist', [])

    for (const tag of tags) {
        let tagInfo = []
        const tagLink = tag.href.split('.org')[1]
        const tagKey = makeTagKey(tagLink)

        tagInfo.push(tagKey)
        tagInfo.push(tagLink)
        tagInfo.push(tag.innerText)

        const tType = await getVal(taglist, tagKey, true) ? 'm' : 'M'

        const container = tag.parentNode
        if (container.tagName !== 'LI') {
            const newContainer = document.createElement('span')
            tag.after(newContainer)
            newContainer.appendChild(tag)
        }
        makeTagTogglebox(tType, tag.parentNode, tagInfo)
    }
}

async function setTitleMute() {
    let blurbId = ''

    async function determineMm(blurbId) {
        if (await getVal('blurblist',blurbId)) {
            return 'm'
        }
        return 'M'
    }

    if (document.querySelectorAll('h2.title.heading').length) { // work or chapter page
        blurbId = getIdFromWorkPage()
        blurbId ? makeTogglebox(false,await determineMm(blurbId),document.querySelector('h2.title.heading')) : null
        if (!await GM.getValue('showTagToggles', true)) addTogglesToWorkTags() // only fire here if the setting is off -- otherwise it's already run
    } else if (document.querySelectorAll('.series-show h2.heading').length) { // series page
        blurbId = getIdFromSeriesPage()
        blurbId ? makeTogglebox(false,await determineMm(blurbId),document.querySelector('.series-show h2.heading')) : null
    } else if (document.querySelector('.profile-show h2.heading') || document.querySelector('.dashboard h2.heading')) { // profile or dashboard
        blurbId = getIdFromProfilePage()
        blurbId ? makeTogglebox(false,'X',document.querySelector('h2.heading')) : null
    }
//     else if (document.querySelector('#main.dashboard') && document.querySelector('#subscription_subscribable_type')?.value === 'User') { // dashboard, logged in
//         blurbId = document.querySelector('#subscription_subscribable_id')?.value
//         makeTogglebox(false, 'X', document.querySelector('.dashboard h2.heading'))
//     }
}

function addToNavBar() {
    let searchNode = document.querySelector('li.search'),
        li = document.createElement('li'),
        a = document.createElement('a')

    a.href = 'javascript:void(0)'
    a.appendChild(document.createTextNode('Muting'))
    a.onclick = function() {
        toggleSettings();
    };

    li.classList.add('dropdown')
    li.appendChild(a)

    searchNode.parentNode.insertBefore(li,searchNode)
}

(async function() {
    'use strict';

    const filterSettings = await getFilterSettings();
    const darkmode = await GM.getValue('darkmode', false);
    const useMobile = (window.innerWidth < 900 ? true : false);

    fontSizing = standardSize

    if (useMobile || await GM.getValue('forceMobile',false)) {fontSizing = mobileSize}

    fontSizing.menuWidth > (window.innerWidth - 10) ? fontSizing.menuWidth = window.innerWidth -10 : ''
    fontSizing.menuHeight > (window.innerHeight -10) ? fontSizing.menuHeight = window.innerHeight -10 : ''

    addToNavBar();
    makeSettings();

    var userlist = await GM.getValue('userlist',[])

    for (const u of userlist) {
        if (u[1] === '.comment') {
            commentMute.push('.comment.' + u[0])
        } else if (!u[1]) {
            commentMute.push('.' + u[0] + ':not(.work,.series)')
        }
    }
    var muteByStyle = commentMute.join(',')

    makeStyleSheet(makeStyleCode(getMenuRules(darkmode)),'AO3-MS-mainStyle');
    makeStyleSheet(filterSettings['custom-css'],'AO3-MS-custom-css');
    makeStyleSheet(makeStyleCode(addMuteRules(commentMute.join(','))),'AO3-MS-muteByStyle')

    await setInitialMute('userlist');
    await setInitialMute('blurblist');
    await setInitialMute('taglist');
    await setTitleMute();
    await setTagMute();
    if (await GM.getValue('showTagToggles', false)) addTogglesToWorkTags()

    addMuteButtonToBlurbs();
})();