EhxVisited

E-H Visited, combined with ExVisited, and then better.

2019-12-26 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         EhxVisited
// @namespace    https://sleazyfork.org/en/users/285675-hauffen
// @version      2.50.43
// @description  E-H Visited, combined with ExVisited, and then better.
// @author       Hauffen
// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @include      /https?:\/\/(e-|ex)hentai\.org\/.*/
// ==/UserScript==

(function() {
    window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: 'readwrite'};
    window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;

    if (!window.indexedDB) {
        console.warn("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
        return;
    }

    /*═════════════════════════════╗
    ║    Configuration Defaults    ║
    ╚═════════════════════════════*/
    var setStore = JSON.parse(localStorage.getItem('ehx-settings')) ? JSON.parse(localStorage.getItem('ehx-settings')) : JSON.parse('{"softHide":false, "minAdd":true, "minShow":false, "cssTT":false, "repPub":false, "visHide":false, "hidShow":false, "pFilter":false, "pLimit":0, "stFilter":false, "stLimit":0, "titleShow":true}');
    var filters = localStorage.getItem('ehx-filters') ? localStorage.getItem('ehx-filters') : '#\\[Erocolor\\]';
    var cssA = JSON.parse(localStorage.getItem('ehx-css')) ? JSON.parse(localStorage.getItem('ehx-css')) : JSON.parse('{"visible":"box-shadow: inset 0 0 0 500px rgba(2, 129, 255, .2) !important;", "hidden":"box-shadow: inset 0 0 0 500px rgba(255, 40, 0, .2) !important;", "filter":"box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;", "page":"box-shadow: inset 0 0 0 500px rgba(0, 0, 180, .2) !important;", "rating":"box-shadow: inset 0 0 0 500px rgba(180, 80, 60, .2) !important;"}');
    var cssD = (setStore.softHide) ? 'opacity:0.2; -webkit-opacity: 0.2;' : 'display: none;';
    /*════════════════════════════*/

    let db = null;
    var filterArr = [];
    var pIS = 0; // Post Infinite Scroll
    var observer = new MutationObserver(e => {
        pIS = true; // Triggered on mutation, should never be false at this point though
        addCSS();
    });

    var spl = document.URL.split('/');
    var d1 = spl[3];
    var galleries, hidden;
    var ehxClearConfirm = 0, ehxhClearConfirm = 0;

    if (d1 == 'g') addGallery(); // Add the current page to galleries
    if (d1.substr(0, 1) == '?' || d1.substr(0, 1) == 'w' || d1.substr(0, 1) == '#' || d1.substr(0, 1) == 'f' || d1.substr(0, 1) == 't' || !d1) {
        populateFilter();
        populate();
    }

    /**
     * Populate the custom filter array with user input converted into regular expressions
	 */
    function populateFilter() {
        if (filters != '') {
            var tempArr = filters.split('\n');
            for(var i = 0; i < tempArr.length; i++) {
                if (tempArr[i].startsWith('#')) continue;
                filterArr.push(new RegExp(tempArr[i], 'i'));
            }
        }
    }

    /**
     * Add a gallery to our IndexedDB
	 */
    function addGallery() {
        const request = indexedDB.open('ehxvisited', 1);

        request.onupgradeneeded = e => { // Generate our database if it's not there
            db = e.target.result;

            if (!db.objectStoreNames.contains('galleries')) {
                db.createObjectStore('galleries', {keyPath: 'id'});
            }
            if (!db.objectStoreNames.contains('hidden')) {
                db.createObjectStore('hidden', {keyPath: 'id'});
            }
        };

        request.onsuccess = e => {
            db = e.target.result;

            var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries');
            var openRequest = objStore.openCursor(spl[4] + '.' + spl[5]);

            openRequest.onsuccess = e => {
                var cursor = openRequest.result;
                if (cursor) { // Update entry if key exists
                    cursor.update({id: spl[4] + '.' + spl[5], visited: Date.now()});
                    console.log('EhxVisited: Updated ' + spl[4] + '.' + spl[5]);
                } else { // Otherwise, add entry
                    objStore.add({id: spl[4] + '.' + spl[5], visited: Date.now()});
                    console.log('EhxVisited: Added ' + spl[4] + '.' + spl[5]);
                }
            };

            openRequest.onerror = e => {
                console.log(`EhxVisited: Something bad happened with gallery ${spl[4]}.${spl[5]}: ${e.target.error}`);
            };
        };
    };

    /**
     * Updates the hidden gallery count in the header object
	 */
    function updateGListing() {
        // There's probably a better way to do this portion rather than iterate through all current objects, but runtime is negligible
        var list = $('.itg .gl1t').length > 0 ? $('.itg .gl1t') : $('table.itg>tbody>tr').has('.glhide, .gldown, th'); // Get the proper elements depending on our view mode
        var hAmount, vAmount, fAmount, pAmount, rAmount, hCount;
        hAmount = vAmount = fAmount = pAmount = rAmount = hCount = 0;
        for (var i = 0; i < list.length; i++) {
            if ($(list[i]).hasClass('ehx-hidden')) { // Count generic Hidden galleries
                if ($(list[i]).hasClass('ehx-visited')) vAmount++;
                hCount++;
            }
            if ($(list[i]).attr('data-jqstyle') != undefined) { // Count elements via JQ Style tags
                if ($(list[i]).attr('data-jqstyle').match('h')) hAmount++;
                if ($(list[i]).attr('data-jqstyle').match('f')) fAmount++;
                if ($(list[i]).attr('data-jqstyle').match('p')) pAmount++;
                if ($(list[i]).attr('data-jqstyle').match('r')) rAmount++;
            }
            if($('.ehx-show').text() === 'Show') { // If Visited Galleries are hidden, count those as well
                if ($(list[i]).hasClass('ehx-visited')) {
                    vAmount++;
                    hCount++;
                }
            }
        }
        if(!setStore.softHide) {
            $('#hideCount').html('There ' + (hCount > 1 || hCount == 0 ? 'are ' : 'is ') + '<span>' + hCount + ' hidden ' + (hCount > 1 || hCount == 0 ? 'galleries' : 'gallery') + '</span> on this page.');
        } else {
            $('#hideCount').html('There are <span>0 hidden galleries</span> on this page.');
        }
        $('#hideCount > span').prop('title', 'Hidden: ' + hAmount + ' | Visited: ' + vAmount + ' | Filtered: ' + fAmount + ' | Page Limit: ' + pAmount + ' | Rating Limit: ' + rAmount);
    }

    /**
     * Convert the star count of a specified element to a double
	 * @param {Object} el - A specific element within the DOM
	 */
    function getStarNumber(el) {
        var stars = $(el).find('.ir').css('background-position');
        switch(stars) {
            case "0px -1px":
                return 5;
            case "0px -21px":
                return 4.5;
            case "-16px -1px":
                return 4;
            case "-16px -21px":
                return 3.5;
            case "-32px -1px":
                return 3;
            case "-32px -21px":
                return 2.5;
            case "-48px -1px":
                return 2;
            case "-48px -21px":
                return 1.5;
            case "-64px -1px":
                return 1;
            case "-64px -21px":
                return 0.5;
            default:
                return 0;
        }
    }

    /**
	 * Check a specified element through the filters individually, then apply jqstyle tags for CSS
	 * @param {Object} el - A specific element within the DOM
	 */
    function filterCheck(el) {
        if (filterArr.length > 0) {
            if(filterArr.some(rx => rx.test($(el).find('.glink').text()))) { // Test our gallery name through our regex filters
                if(!$(el).hasClass('ehx-hidden')) {
                    $(el).addClass('ehx-hidden');
                }
                addStyle($(el), 'f');
            }
        }

        // Filter our galleries through the star limit filter
        if (setStore.stFilter && setStore.stLimit > 0) {
            if (getStarNumber(el) < setStore.stLimit) {
                if(!$(el).hasClass('ehx-hidden')) {
                    $(el).addClass('ehx-hidden');
                }
                addStyle($(el), 'r');
            }
        }
        var pages = 0; // Page Count is stored in a lot of different random elements throughout, there are probably better selectors for this
        if ($('.gl1e').length) { pages = $(el).find('.gl3e > div:nth-child(5)').text().split(' ')[0]; }
        else if ($('.gl1c').length) { pages = $(el).find('.gl4c > div:nth-child(2)').text().split(' ')[0]; }
        else if ($('.gl1t').length) { pages = $(el).find('.gl5t > div:nth-child(2) > div:nth-child(2)').text().split(' ')[0]; }
        else { pages = $(el).find('.gl2m > div:nth-child(2) > div:nth-child(2) > div:nth-child(2) > div:nth-child(2)').text().split(' ')[0]; }
        if (setStore.pFilter && setStore.pLimit > 0) {
            if(parseInt(pages) < parseInt(setStore.pLimit)) {
                if(!$(el).hasClass('ehx-hidden')) {
                    $(el).addClass('ehx-hidden');
                }
                addStyle($(el), 'p');
            }
        }
    }

    /**
     * Adds the specified style flag to a specified element
	 * @param {Object} el - A specific element within the DOM
	 * @param {String} flag - A character to mark an element for JQ Styling CSS rules
	 */
    function addStyle(el, flag) {
        if ($(el).attr('data-jqstyle') != undefined) {
            $(el).attr('data-jqstyle', $(el).attr('data-jqstyle') + flag);
        } else {
            $(el).attr('data-jqstyle', flag);
        }
    }

    /**
     * Removes the specified style flag from a specified element
	 * @param {Object} el - A specific element within the DOM
	 * @param {string} flag - A JQ Style flag to remove from an element
	 */
    function removeStyle(el, flag) {
        if ($(el).attr('data-jqstyle') != undefined) {
            $(el).attr('data-jqstyle', $(el).attr('data-jqstyle').replace(flag, ''));
        } else {
            $(el).attr('data-jqstyle', '');
        }
    }

    /**
     * Toggles a specified element's hidden status
	 * @param {String} tga - Full gallery URL
	 * @param {Object} el - A specific element within the DOM
	 */
    function toggleElement(tga, el) {
        const request = indexedDB.open('ehxvisited', 1);
        var tgid = tga.split('/')[4] + '.' + tga.split('/')[5];

        request.onsuccess = e => {
            db = e.target.result;
            var objStore = db.transaction('hidden', 'readwrite').objectStore('hidden');
            var openReq = objStore.openCursor(tgid);
            openReq.onsuccess = e => {
                var cursor = e.target.result;
                if (cursor) { // Gallery already exists within our hidden table
                    cursor.delete();
                    console.log('EhxVisited: Removed ' + tgid + ' from hidden list.');
                    $(el).removeClass('ehx-hidden');
                    removeStyle($(el), 'h');
                    delete hidden.data[tgid]; // Remove gallery listing from our local store of hidden galleries
                } else {
                    objStore.put({id: tgid}); // Put the gallery into our hidden table
                    console.log('EhxVisited: Added ' + tgid + ' to hidden list.');
                    $(el).addClass('ehx-hidden');
                    addStyle($(el), 'h');
                    hidden.data[tgid] = 1; // Add gallery listing to our local store of hidden galleries
                }
                updateGListing();
                $('#hLength').text(Object.keys(hidden.data).length);
            }
        }
    }

    /**
	 * Fill our local gallery listings so we can preform easier operations on the data.
	 * Also set up the majority of our global HTML elements and their functions.
	 */
    function populate() { // TODO: Separate the HTML entries from the population portion
		galleries = JSON.parse('{"data":{}}');
		hidden = JSON.parse('{"data":{}}');
        const request = indexedDB.open('ehxvisited', 1);

        request.onupgradeneeded = e => {
            db = e.target.result;

            if (!db.objectStoreNames.contains('galleries')) {
                db.createObjectStore('galleries', {keyPath: 'id'});
            }
            if (!db.objectStoreNames.contains('hidden')) {
                db.createObjectStore('hidden', {keyPath: 'id'});
            }
        };

        request.onsuccess = e => {
            db = e.target.result;
            var objStore = db.transaction('galleries', 'readonly').objectStore('galleries');
            var openReq = objStore.getAll();
            openReq.onsuccess = f => {
                console.log('EhxVisited: Populated global variables.');
                var transform = f.target.result;
                for (var i = 0; i < transform.length; i++) {
                    galleries.data[transform[i].id] = transform[i].visited; // Force matrix data into array data
                }
                var gLength = Object.keys(galleries.data).length
                var objStore2 = db.transaction('hidden', 'readonly').objectStore('hidden');
                var openReq2 = objStore2.getAll();
                openReq2.onsuccess = g => {
                    var transform2 = g.target.result;
                    for (i = 0; i < transform2.length; i++) {
                        hidden.data[transform2[i].id] = 1; // Force matrix data into array data
                    }
                    var hLength = Object.keys(hidden.data).length
                    $('#toppane').append('<ehx class="ehx-controls">Galleries visited: <span id="gLength">' + gLength + '</span> ( <span id="ehx-menu-control"></span><a href="javascript:;" class="ehx-settings">Settings</a> )<br/>Hidden Galleries: <span id="hLength">' + hLength + '</span><span id="ehxh-menu-control"></span></ehx>');
                    if (!setStore.softHide) {
                        $('#ehx-menu-control').append('<a href="javascript:;" class="ehx-show">Hide</a> / ');
                        $('#ehxh-menu-control').append(' ( <a href="javascript:;" class="ehxh-show">Show</a> )');
                    }
                    $('.ehx-settings').click(e => {
                        e.preventDefault();
                        settings();
                        return false;
                    });
                    $('.ehx-show').click(e => {
                        var disp =  $('.ehx-visited.ehx-hidden').css('display');
                        if ($('.ehx-show').text() === 'Show') {
                            $('.ehx-visited').css({display: ''});
                            $('.ehx-visited.ehx-hidden').css({display: disp});
                        } else {
                            $('.ehx-visited').css({display: 'none'});
                            $('.ehx-visited.ehx-hidden').css({display: disp});
                        }
                        $('.ehx-show').text((i, t) => {
                            return t === 'Show' ? 'Hide' : 'Show';
                        });
                        updateGListing();
                        setStore.visHide = $('.ehx-show')[0].innerText === 'Show' ? true : false;
                        localStorage.setItem('ehx-settings', JSON.stringify(setStore)); // Update our stored settings
                    });
                    $('.ehxh-show').click(e => {
                        if ($('.ehxh-show').text() === 'Show') {
                            if ($('.gl1t').length == 0) { // For table view modes
                                $('.ehx-hidden').css({display: $('.ehx-hidden').parent().find('tr').not('.ehx-hidden').css('display')}); // Copy the display CSS of our closest element
                                $('.ehx-visited.ehx-hidden').css({display: $('.ehx-hidden').parent().find('tr').not('.ehx-hidden').css('display')});
                            } else { // Default display CSS for thumbnail
                                $('.ehx-hidden').css({display: 'flex'});
                                $('.ehx-visited.ehx-hidden').css({display: 'flex'});
                            }
                        } else {
                            $('.ehx-hidden').css({display: ''});
                            $('.ehx-visited.ehx-hidden').css({display: ''});
                        }
                        $('.ehxh-show').text((i, t) => { // Toggle text
                            return t === 'Show' ? 'Hide' : 'Show';
                        });
                        updateGListing();
                        setStore.hidShow = $('.ehxh-show')[0].innerText === 'Hide' ? true : false;
                        localStorage.setItem('ehx-settings', JSON.stringify(setStore));
                    });
                    $('.ehx-controls').append('<br /><span id="hideCount"><span></span></span>');
					addCSS();
					updateGListing();
                }
            }
        }
    }

    /**
	 * Our main function that does basically everything that we see.
	 * Appends our custom HTML objects to the main page.
	 * Adds CSS to elements based on whether they can be found in the populated local gallery listing.
	 */
    function addCSS() { // TODO: Put all the time difference lines in some kind of function to clean this up
        observer.disconnect(); // Disconnect the observer, or else all the CSS changes will trigger the mutation observer
        var img_hide = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg==';
        var list = $('table.itg>tbody>tr').has('.glhide, .gldown, th'); // Present only in list views
        var thumb = $('.itg .gl1t'); // Present only in thumbnail view
        var gid, galleryId, onFavs, d;

        if (list.length > 0) {
            if ($('.gl1e').length) { // Extended
                if ($('h1').text() === 'Favorites') {
                    onFavs = 1;
                }
                for (var i = 0; i < list.length; i++) {
                    gid = $(list[i]).find('.gl1e a').attr('href').split('/');
                    galleryId = gid[4] + '.' + gid[5];
                    if ($(list[i])[0].children.length === 2 && onFavs) {
                        $(list[i]).append('<td></td>');
                    }
                    if(galleries.data[galleryId] != undefined) {
                        if (!$(list[i]).hasClass('ehx-visited')) {
                            d = new Date(galleries.data[galleryId]);
                            $(list[i]).addClass('ehx-visited');
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                            if (setStore.repPub) $(list[i]).find('.gl3e div:nth-child(2)').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'));
                            // Check for fav pages
                            if ($(list[i]).find('.gl3e').children('div').length >= 7) { // Date favourited div is present
                                $(list[i]).find('.gl3e > div:last-child').append('<br><ehx class="ehx-extended-favs">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + '<br>' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')</ehx>');
                            } else {
                                $(list[i]).find('.gl3e').append('<ehx class="ehx-extended">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + '<br>' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')</ehx>');
                            }
                        }
                    } else { // Hidden
                        if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited');
                        if (setStore.repPub) $(list[i]).find('.gl3e div:nth-child(2)').text('Never Visited');
                    }
                    if(hidden.data[galleryId] != undefined) {
                        if(!$(list[i]).hasClass('ehx-hidden')) {
                            $(list[i]).addClass('ehx-hidden');
                        }
                        addStyle($(list[i]), 'h');
                    }
                    if($(list[i])[0].childElementCount < 3) { // Don't append anything if it's already been appended
                        $('<img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; padding: 4px 0px 0px 4px;" title="Show/Hide gallery">').prependTo($(list[i]).find('a').first().parent().parent().parent()).on('click', e => {
                            var el = $(e.currentTarget).parent();
                            toggleElement($(el).find('a').attr('href'), $(el));
                        })
                    }
                    filterCheck($(list[i]));
                }
                updateGListing();
            } else if ($('.gl1c').length) { // Compact
                var borderColour = $('.gl1c').first().css('border-top-color'); // Border colour is different between domains
                if (!pIS && $('table.itg tbody>tr:first-child')[0].children.length < 5) { // Apend our table columns to the table head
                    $('table.itg tbody>tr:first-child th:nth-child(4)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">&#x2716</th>');
                    if (setStore.minAdd) {
                        $('table.itg tbody>tr:first-child th:nth-child(2)').after('<th>Visited</th>');
                    }
                    if (setStore.repPub) {
                        $('table.itg tbody>tr:first-child')[0].children[1].innerText = 'Visited'
                    }
                    pIS = true;
                }
                if ($('h1').text() === 'Favorites') {
                    onFavs = 1;
                }
                for (i = 1; i < list.length; i++) {
                    gid = $(list[i]).find('.glname a').attr('href').split('/');
                    galleryId = gid[4] + '.' + gid[5];
                    if ($(list[i])[0].children.length === 4 || $(list[i])[0].children.length === 5 && onFavs) {
                        if ($(list[i])[0].children.length === 4 && onFavs) {
                            $(list[i]).append('<td></td>');
                        }
                        if(galleries.data[galleryId] != undefined) {
                            d = new Date(galleries.data[galleryId]);
                            $(list[i]).addClass('ehx-visited');
                            if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'));
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                            if (setStore.minAdd) $(list[i]).children('.gl2c').after('<td class="ehx-compact" style="border-color: ' + borderColour + ';"><ehx>' + timeDifference(galleries.data[galleryId], true) + '<br>(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')<br>' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx></td>');
                        } else {
                            if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text('Never Visited');
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited');
                            if (setStore.minAdd) $(list[i]).children('.gl2c').after('<td class="ehx-compact" style="border-color:' + borderColour + ';"></td>');
                        }
                    } else {
                        if (galleries.data[galleryId] != undefined) {
                            d = new Date(galleries.data[galleryId]);
                            $(list[i]).addClass('ehx-visited');
                            if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'));
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                            if (setStore.minAdd) $(list[i]).find('.ehx-compact').html('<ehx>' + timeDifference(galleries.data[galleryId], true) + '<br>(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')<br>' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx>');
                        }
                    }
                    if(hidden.data[galleryId] != undefined) {
                        if(!$(list[i]).hasClass('ehx-hidden')) {
                            $(list[i]).addClass('ehx-hidden');
                        }
                        addStyle($(list[i]), 'h');
                    }
                    if($(list[i])[0].childElementCount < 5 || ($(list[i])[0].childElementCount < 6 && setStore.minAdd)) { // Don't append anything if it's already been appended
                        $('<td class="hideContainer" style="border-bottom: 1px solid #6f6f6f4d; border-top: 1px solid #6f6f6f4d;"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; vertical-align: center;" title="Show/Hide gallery"></td>').appendTo($(list[i]).find('a').first().closest('tr')).on('click', e => {
                            var el = $(e.currentTarget).closest('tr');
                            toggleElement($(el).find($('.gl3c > a')).attr('href'), $(el));
                        })
                    }
                    filterCheck($(list[i]));
                }
                updateGListing();
            } else { // Minimal
                if(!pIS) { // Append our table columns to the table head
                    $('table.itg tbody>tr:first-child th:nth-child(6)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">&#x2716</th>');
                    if (setStore.minAdd) {
                        $('table.itg tbody>tr:first-child th:nth-child(2)').after('<th title="EhxVisited: Hover for timestamps" style="text-align: center;">\uD83D\uDC41</th>');
                    }
                    if (setStore.repPub) {
                        $('table.itg tbody>tr:first-child')[0].children[1].innerText = 'Visited'
                    }
                    pIS = true;
                }
                if ($('h1').text() === 'Favorites') {
                    onFavs = 1;
                }
                for (i = 1; i < list.length; i++) {
                    gid = $(list[i]).find('.glname a').attr('href').split('/');
                    galleryId = gid[4] + '.' + gid[5];
                    if(hidden.data[galleryId] != undefined) {
                        if(!$(list[i]).hasClass('ehx-hidden')) {
                            $(list[i]).addClass('ehx-hidden');
                        }
                        addStyle($(list[i]), 'h');
                    }
                    if ($(list[i])[0].children.length === 6 || $(list[i])[0].children.length === 7 && onFavs) {
                        if ($(list[i])[0].children.length === 6 && onFavs) {
                            $(list[i]).append('<td></td>');
                        }
                        if (galleries.data[galleryId] != undefined) {
                            d = new Date(galleries.data[galleryId]);
                            $(list[i]).addClass('ehx-visited');
                            if (setStore.minAdd) { // Append viewed column
                                if (setStore.minShow) { // Show text in appended column
                                    $(list[i]).children('.gl2m').after('<td class="ehx-minimal-text"><ehx title="(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '">' + timeDifference(galleries.data[galleryId], true) + '</ehx></td>');
                                } else { // Show icon in appended column
                                    $(list[i]).children('.gl2m').after('<td class="ehx-minimal" title="EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '"><ehx>\uD83D\uDC41</ehx></td>');
                                }
                            }
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                            if (setStore.repPub) $(list[i]).find('.gl2m').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'));
                        } else { // Not viewed
                            if (setStore.minAdd) $(list[i]).children('.gl2m').after('<td class="' + (setStore.minShow ? 'ehx-minimal-text' : 'ehx-minimal') + '"></td>'); // Only append another table cell to the row
                            if (setStore.repPub) $(list[i]).find('.gl2m').text('Never Visited');
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited');
                        }
                        if($(list[i])[0].childElementCount < 8 || ($(list[i])[0].childElementCount < 8 && !setStore.minAdd)) { // Don't append anything if it's already been appended // I'll have to look at this conditional again, seems redundant
                            $('<td class="hideContainer"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; vertical-align: middle;" title="Show/Hide gallery"></td>').appendTo($(list[i]).find('a').first().closest('tr')).on('click', e => {
                                var el = $(e.currentTarget).closest('tr');
                                toggleElement($(el).find($('.gl3m > a')).attr('href'), $(el));
                            })
                        }
                    } else {
                        if (galleries.data[galleryId] != undefined) {
                            d = new Date(galleries.data[galleryId]);
                            $(list[i]).addClass('ehx-visited');
                            if (setStore.minAdd)  {
                                if (setStore.minShow) {
                                    $(list[i]).find('.ehx-minimal-text').html('<ehx title="(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '">' + timeDifference(galleries.data[galleryId], true) + '</ehx>');
                                } else {
                                    $(list[i]).find('.ehx-minimal').html('<ehx>\uD83D\uDC41</ehx>');
                                    $(list[i]).find('.ehx-minimal').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                                }
                            }
                            if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                            if (setStore.repPub) $(list[i]).find('.gl2m').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'));
                        }
                    }
                    filterCheck($(list[i]));
                }
                updateGListing();
            }
        } else if (thumb.length > 0) { // Thumbnail
            for (i = 0; i < thumb.length; i++) {
                gid = $(thumb[i]).find('.gl3t a').attr('href').split('/');
                galleryId = gid[4] + '.' + gid[5];
                if(galleries.data[galleryId] != undefined) {
                    if (!$(thumb[i]).hasClass('ehx-visited')) {
                        d = new Date(galleries.data[galleryId]);
                        $(thumb[i]).addClass('ehx-visited');
                        if (setStore.cssTT) {
                            $(thumb[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate());
                        }
                        $(thumb[i]).children('.gl5t').after('<ehx class="ehx-thumbnail">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx>');
                    }
                } else {
                    if (setStore.cssTT) {
                        $(thumb[i]).find('.glname').attr('title', 'Never Visited');
                    }
                }
                if(hidden.data[galleryId] != undefined) {
                    if(!$(thumb[i]).hasClass('ehx-hidden')) {
                        $(thumb[i]).addClass('ehx-hidden');
                    }
                    addStyle($(thumb[i]), 'h');
                }
                if($(thumb[i]).find('.gl5t').children().length < 3) { // Don't append anything if it's already been appended
                    $('<div class="hideContainer"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; position: absolute; bottom: 3px; left: 2px;" title="Show/Hide gallery"></div>').appendTo($(thumb[i]).find('.gl5t')).on('click', e => {
                        var el = $(e.currentTarget).parent().parent();
                        toggleElement($(el).find('a').attr('href'), $(el));
                    })
                }
                filterCheck($(thumb[i]));
            }
            updateGListing();
        } else {
            console.log('EhxVisited: Something went wrong or an invalid view'); // This happens when you use empty() on itg, which can happen with other scripts
        }

        if (setStore.visHide) {
            $('.ehx-visited').css({display: 'none'});
            $('.ehx-show').text('Show');
        }
        if (setStore.hidShow) {
            if ($('.ehx-hidden').length < 25) { $('.ehx-hidden').css({display: $('.ehx-hidden').siblings().not('.ehx-hidden').css('display')}) } // Make sure there are elements on the page
            else { // Unless you're an idiot and hid everything on the page
                if ($('.gl1t').length) { $('.ehx-hidden').css({display: 'flex'}); } // Use the default values
                else { $('.ehx-hidden').css({display: 'table-row'}); }
            }
            $('.ehxh-show').text('Hide');
        }
        observer.observe($('.itg').get(0), { // Reconnect the observer for changes
            childList: true,
            subtree: true
        });
    }

    /**
	 * Get time difference in words
	 * @param {Date} previous - Previous date to compare against Date.now()
	 * @param {Boolean} abbreviate - Should the text string have abbreviatated text
	 */
    function timeDifference(previous, abbreviate) {
        var msPerMinute = 60 * 1000;
        var msPerHour = msPerMinute * 60;
        var msPerDay = msPerHour * 24;
        var msPerMonth = msPerDay * 30;
        var msPerYear = msPerDay * 365;
        var elapsed = Date.now() - previous;

        if (elapsed < msPerMinute) {
            return Math.round(elapsed / 1000) + ((typeof abbreviate !== 'undefined') ? '&nbsp;sec' : ' seconds ago');
        } else if (elapsed < msPerHour) {
            return Math.round(elapsed / msPerMinute) + ((typeof abbreviate !== 'undefined') ? '&nbsp;min' : ' minutes ago');
        } else if (elapsed < msPerDay) {
            return Math.round(elapsed / msPerHour) + ((typeof abbreviate !== 'undefined') ? '&nbsp;hrs' : ' hours ago');
        } else if (elapsed < msPerMonth) {
            return Math.round(elapsed / msPerDay) + ((typeof abbreviate !== 'undefined') ? '&nbsp;days' : ' days ago');
        } else if (elapsed < msPerYear) {
            return Math.round(elapsed / msPerMonth) + ((typeof abbreviate !== 'undefined') ? '&nbsp;mos' : ' months ago');
        } else {
            return Math.round(elapsed / msPerYear) + ((typeof abbreviate !== 'undefined') ? '&nbsp;yrs' : ' years ago');
        }
    }

	/**
	 * Displays a div at the top of the page with a message
	 * @param {String} message - A message to be displayed within the alert
	 * @param {Integer} timeout - Millseconds message should be displayed for
	 */
	function displayAlert(message, timeout) {
		var alert = $(`<div class="notice">EhxVisited: ` + message + `</div>`);
		$(alert).hide().appendTo('body').fadeIn(1000);
		setTimeout(e => { $('.notice').fadeOut(1000, f => { $('.notice').remove(); }); }, timeout);
	}

    /**
	 * Fills a text area with formatted gallery data for export
	 * @param {IndexedDB Matrix} items - Raw IndexedDB getAll output
	 */
    function ehxExport(items) {
        var data = '';
        for (var i in items) {
            data += items[i].id + ':' + items[i].visited + ';';
        }
        $('#exportGalleries').val(''); // Clear text area
        $('#exportGalleries').val(data); // Fill with formatted data
		$('#exportGalleries').select();
    }

    $('.itg').on('mousedown', 'a', e => {
        if (e.which === 3) return; // Ignore right-clicks
        if (e.currentTarget.href.split('/')[3] === 'g') {
            galleries.data[e.currentTarget.href.split('/')[4] + '.' + e.currentTarget.href.split('/')[5]] = Date.now();
            $('#gLength').text(Object.keys(galleries.data).length);
            addCSS();
        }
    });

    /**
	 * Import user data into our indexedDB
	 * @param {String} store - An object store within the indexedDB
	 * @param {JSON Array} items - String of exported data to import
	 */
    function ehxImport(store, items) {
        var objStore2 = db.transaction(store, 'readwrite').objectStore(store);
        var count = 0, sp = '';

        sp = items.split(';');
        sp = sp.filter(Boolean); // Filter out any null ('') entries
        insertNext();

        /**
		 * Push entries into the specified indexedDB store
		 */
        function insertNext() {
            if (count < sp.length) {
                var str = sp[count].split(':');
                objStore2.put({id: str[0], visited: parseInt(str[1])}).onsuccess = insertNext; // Update the record if it's there, or add it if it's not, then continue
                ++count;
            } else {
				displayAlert('Imported ' + count + ' entries', 5000);
                console.log('EhxVisited: Merge Completed');
                $('#importGalleries').val('');
				populate();
            }
        }
    }

    /**
	 * Open the Settings menu and set up all necessary menu functions
	 */
    function settings() {
        const req = indexedDB.open('ehxvisited', 1);

        req.onsuccess = e => {
            if (db == null) db = e.target.result;
            var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries');
            var openReq = objStore.getAll();
            openReq.onsuccess = e => {
                var len = e.target.result;
                // There's probably a much easier way to do this, or at least a nicer looking, more technical way
                var container = $(`
                <div class="overlay">
					<div class="settings">
						<nav class="topNav">
							<span style="float: left; margin-left: 3px; font-weight: lighter; opacity: 0.5; -webkit-opacity: 0.5;">All Applied Settings Will Take Effect On Close</span>
							<div>
								<div class="mencon">
									<button class="menu">Export</button>
									<div class="dropdown">
										<a id="ehx-export" href="javascript:;">Export Galleries</a>
										<a id="ehxh-export" href="javascript:;">Export Hidden Galleries</a>
									</div>
								</div>
								<div class="mencon">
									<button class="menu">Import</button>
									<div class="dropdown">
										<a id="ehx-import" href="javascript:;">Import Galleries</a>
										<a id="ehxh-import" href="javascript:;">Import Hidden Galleries</a>
									</div>
								</div>
								<a id="settings-close" href="javascript:;">&#128939</a>
							</div>
						</nav>
						<div class="section-container">
							<section>
								<fieldset>
									<legend>Settings</legend>
									<div>
										<label>
											<input type="checkbox" id="softHide" ` + (setStore.softHide ? `checked` : ``) + `>Soft Hide Galleries
										</label>
										<span>: Darken hidden galleries instead of removing them from view</span>
									</div>
									<div>
										<label>
											<input type="checkbox" id="minAdd" ` + (setStore.minAdd ? `checked` : ``) + `>Add Visited Column
										</label>
										<span>: Show visits in an additional column in Minimal/Minimal+ and Compact view modes</span>
										<div class="suboptions">
											<div>
												<span class="branch">&#8735</span>
												<label>
													<input type="checkbox" id="minShow" ` + (setStore.minShow ? `checked` : ``) + `>Minimal Show Text
												</label>
												<span>: Show visits as text instead of hovering tooltip in Minimal/Minimal+ view modes</span>
											</div>
										</div>
									</div>
									<div>
										<label>
											<input type="checkbox" id="cssTT" ` + (setStore.cssTT ? `checked` : ``) + `>CSS Tooltips
										</label>
										<span>: Replace gallery link tooltips with visited information in all view modes</span>
									</div>
									<div>
										<label>
											<input type="checkbox" id="repPub" ` + (setStore.repPub ? `checked` : ``) + `>Replace Published
										</label>
										<span>: Replace date published with date visited in Minimal/Minimal+ view modes</span>
									</div>
									<div>
										<label>
											<input type="checkbox" id="titleShow" ` + (setStore.titleShow ? `checked`: ``) + `>Show Full Title
										</label>
										<span>: Show the full title of a gallery on hover in Thumbnail view</span>
									</div>
								</fieldset>
								<fieldset>
									<legend>Custom CSS</legend>
									<h3>Visited Galleries
										<div class="control" id="visControls">
											<button id="resV">Reset CSS</button>
											<button class="ehx-clear">Clear Data</button>
										</div>
									</h3>
									<textarea id="visited" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.visible + `</textarea>
									<h3>Hidden Galleries
										<div class="control sControls" id="hideControls">
											<button id="resH">Reset CSS</button>
											<button class="ehxh-clear">Clear Data</button>
										</div>
									</h3>
									<textarea id="hidden" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.hidden + `</textarea>
									<div class="suboptions2">
										<button class="collapsible">Filtered Galleries</button>
										<div class="content">
											<textarea id="filtered" class="field" spellcheck="false">` + cssA.filter + `</textarea>
											<div class="control sControls">
												<button id="resF">Reset CSS</button>
											</div>
										</div>
										<button class="collapsible">Page Filtered</button>
										<div class="content">
											<textarea id="page" class="field" spellcheck="false"placeholder="Insert CSS">` + cssA.page + `</textarea>
											<div class="control sControls">
												<button id="resP">Reset CSS</button>
											</div>
										</div>
										<button class="collapsible">Rating Filtered</button>
										<div class="content">
											<textarea id="rating" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.rating + `</textarea>
											<div class="control sControls">
												<button id="resR">Reset CSS</button>
											</div>
										</div>
									</div>
								</fieldset>
								<fieldset>
									<legend>Filters</legend>
									Use one <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">regular expression</a> per line to filter out matching galleries.
									<ul style="margin: 3px 0px; padding-left: 30px;">
										<li>E.G. <code>Ongoing</code> will filter out every gallery with <code>ongoing</code>, case-insensitive, in the title. <code>\\[Digital\\]</code> will filter out every gallery with <code>[Digital]</code>, case-insensitive, in the title.</li>
										<li>Lines starting with <code>#</code> will be ignored.</li>
									</ul>
									<textarea id="galFilter">` + filters + `</textarea>
									<div>
										<label>
											<input type="checkbox" id="pFilt" ` + (setStore.pFilter ? `checked` : ``) + `>Page Limit
										</label>
										<span>: Filter out any gallery with pages less than:
											<input id="pLim" type="number" min="1" value="` + setStore.pLimit + `" ` + (setStore.pFilter ? `` : `disabled`) + `/>
										</span>
									</div>
									<div>
										<label>
											<input type="checkbox" id="stFilt" ` + (setStore.stFilter ? `checked` : ``) + `>Minimum Rating
										</label>
										<span>: Filter out any gallery with a rating less than:
											<select id="stLim" ` + (setStore.stFilter ? `` : `disabled`) + `>
												<option>5</option>
												<option>4.5</option>
												<option>4</option>
												<option>3.5</option>
												<option>3</option>
												<option>2.5</option>
												<option>2</option>
												<option>1.5</option>
												<option>1</option>
											</select>
										</span>
									</div>
								</fieldset>
							</section>
							<section class="inactive">
								<fieldset style="padding-bottom: 2px; min-height: 474px;">
									<legend id="importTitle">Import Galleries</legend>
									<textarea id="importGalleries"></textarea>
									<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
										<button class="cancel">Cancel</button>
										<button id="importConfirm">Import</button>
									</div>
								</fieldset>
							</section>
							<section class="inactive">
								<fieldset style="padding-bottom: 2px; min-height: 474px;">
									<legend id="exportTitle">Export Galleries</legend>
									<textarea id="exportGalleries"></textarea>
									<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
										<button class="cancel">Cancel</button>
										<button id="exportCopy">Copy</button>
									</div>
								</fieldset>
							</section>
						</div>
						<div class="applyContainer">
							<div class="control" style="padding-right: 5px;">
								<button id="apply">Apply</button>
							</div>
						</div>
					</div>
				</div>`);
                $('body').append(container);
                if (!$('#minAdd').prop('checked')) { $('#minShow').prop('disabled', true); }
                $('#resV').click(e => { $('#visited').val('box-shadow: inset 0 0 0 500px rgba(2, 129, 255, .2) !important;'); }); // Default Values
                $('#resH').click(e => { $('#hidden').val('box-shadow: inset 0 0 0 500px rgba(255, 40, 0, .2) !important;'); });
                $('#resF').click(e => { $('#filtered').val('box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;'); });
                $('#resP').click(e => { $('#page').val('box-shadow: inset 0 0 0 500px rgba(0, 0, 180, .2) !important;'); });
                $('#resR').click(e => { $('#rating').val('box-shadow: inset 0 0 0 500px rgba(180, 80, 60, .2) !important;'); });
                $('#stLim').val(setStore.stLimit);
                $(document).on('change', 'input', e => { // Put the change listener on document since I suck at event propogation and bubbling
                    if ($('#minAdd').prop('checked')) {
                        $('#minShow').prop('disabled', false);
                    } else {
                        $('#minShow').prop('disabled', true);
                    }
                    if ($('#pFilt').prop('checked')) {
                        $('#pLim').prop('disabled', false);
                    } else {
                        $('#pLim').prop('disabled', true);
                    }
                    if ($('#stFilt').prop('checked')) {
                        $('#stLim').prop('disabled', false);
                    } else {
                        $('#stLim').prop('disabled', true);
                    }
                });
                $('#settings-close').click(e => location.reload()); // The price I have to pay for being bad at doing things dynamically
                $('body').click(e => {
                    if (e.target.className == "overlay") { // Exit if settings menu isn't clicked
                        location.reload();
                    } else if (e.target.className != 'show' && e.target.className != 'menu') {
                        $('.show').removeClass('show');
                    }
                });
				$('#apply').click(e => applySettings());

                /**
				 * Parse our HTML options into a temporary JSON array and then stringify it into localStorage
				 */
                function applySettings() {
                    var tempSto = {
                        "softHide": $('#softHide').prop('checked'),
                        "minAdd": $('#minAdd').prop('checked'),
                        "minShow": $('#minShow').prop('checked'),
                        "cssTT": $('#cssTT').prop('checked'),
                        "repPub": $('#repPub').prop('checked'),
                        "visHide": $('.ehx-show').text() === "Show" ? true : false,
                        "hidShow": $('.ehxh-show').text() === "Hide" ? true : false,
                        "pFilter": $('#pFilt').prop('checked'),
                        "pLimit": $('#pFilt').prop('checked') ? $('#pLim').val() : "0",
                        "stFilter": $('#stFilt').prop('checked'),
                        "stLimit": $('#stFilt').prop('checked') ? $('#stLim option:selected').text() : "0",
                        "titleShow": $('#titleShow').prop('checked')
                    }
                    localStorage.setItem('ehx-settings', JSON.stringify(tempSto)); // Write settings to localStorage
                    var tempCss = {
                        "visible": $('#visited').val(),
                        "hidden": $('#hidden').val(),
                        "filter":  $('#filtered').val(),
                        "page": $('#page').val(),
                        "rating": $('#rating').val()
                    }
                    localStorage.setItem('ehx-css', JSON.stringify(tempCss));
                    localStorage.setItem('ehx-filters', $('#galFilter').val().replace(/^\s*[\r\n]/gm, '')); // Remove null entries because bad things happen if they're there
                    displayAlert('Applied Current Settings', 5000);
                }

                $('.collapsible').click(e => { // Expand our custom filtering CSS boxes
                    if ($('.active').length && !$('.active').is(e.target)) { // If a menu is open and it isn't the one we're clicking, close it
                        $('.active').next().css('max-height', '');
                        $('.active').toggleClass('active');
                    }
                    e.target.classList.toggle('active');
                    var content = e.target.nextElementSibling;
                    if (content.style.maxHeight){
                        content.style.maxHeight = null;
                    } else {
                        content.style.maxHeight = '500px';
                    }
                });

                $('#ehx-import').click(e => {
                    $('.section-container section').addClass('inactive');
					$('.section-container section:nth-child(2)').removeClass('inactive');
					$('#importTitle').text('Import Galleries');
					$('#importGalleries').val('');
				});
                $('#ehxh-import').click(e => {
                    $('.section-container section').addClass('inactive');
					$('.section-container section:nth-child(2)').removeClass('inactive');
					$('#importTitle').text('Import Hidden Galleries');
					$('#importGalleries').val('');
				});
				$('#importConfirm').click(e => ehxImport($('#importTitle').text().search(/hidden/i) != -1 ? 'hidden' : 'galleries', $('#importGalleries').val().replace(/^\s*[\r\n]/gm, '')));
				$('.cancel').click(e => {
                    $('.section-container section').addClass('inactive');
					$('.section-container section:first-child').removeClass('inactive');
				});

				$('#exportCopy').click(e => {
					$('#exportGalleries').select();
					document.execCommand('copy');
					displayAlert('Copied Text To Clipboard', 5000);
				});

                $('#ehx-export').click(e => {
                    $('.section-container section').addClass('inactive');
					$('.section-container section:nth-child(3)').removeClass('inactive');
					$('#exportTitle').text('Exported Galleries');
                    ehxExport(len);
                });

                $('#ehxh-export').click(e => {
                    var objStore2 = db.transaction('hidden', 'readwrite').objectStore('hidden');
                    var openReq = objStore2.getAll();
                    openReq.onsuccess = e => {
                        $('.section-container section').addClass('inactive');
						$('.section-container section:nth-child(3)').removeClass('inactive');
						$('#exportTitle').text('Exported Hidden Galleries');
                        ehxExport(e.target.result);
                    }
                });

                $('.ehx-clear').click(e => {
                    if (!ehxClearConfirm) { // Make sure to double check before deleting
                        ehxClearConfirm = 1;
                        $('.ehx-clear').append(': Are you sure?');
                    } else {
                        var objStore2 = db.transaction('galleries', 'readwrite').objectStore('galleries');
                        var openReq = objStore2.clear();
                        openReq.onsuccess = e => {
                            displayAlert('Cleared all entries', 5000);
							$('.ehx-clear').text('Clear Data');
                        }
                    }
                });

                $('.ehxh-clear').click(e => {
                    var objStore2 = db.transaction('hidden', 'readwrite').objectStore('hidden');
                    var openReq = objStore2.getAll();
                    openReq.onsuccess = e => {
                        if (!ehxhClearConfirm) { // Make sure to double check before deleting
                            ehxhClearConfirm = 1;
                            $('.ehxh-clear').append(': Are you sure?');
                        } else {
                            var objStore3 = db.transaction('hidden', 'readwrite').objectStore('hidden');
                            var openReq = objStore3.clear();
                            openReq.onsuccess = e => {
                                displayAlert('Cleared all entries', 5000);
								$('.ehxh-clear').text('Clear Data');
                            }
                        }
                    }
                });

                // Fancy function to make sure there's not more than one top menu item open
                $('.menu').click(e => {
                    if ($('.show').length) {
                        if ($('.show').prev().is(e.target)) { $(e.target).next().toggleClass('show'); }
                        else {
                            $('.show').removeClass('show');
                            $(e.target).next().toggleClass('show');
                        }
                    } else {
                        $(e.target).next().toggleClass('show');
                    }
                });
            }
        }
    }
    // The giant CSS block
    $(`<link rel="stylesheet" media="screen" href="https://fontlibrary.org/face/symbola" type="text/css"/>
    <style data-jqstyle='ehxVisited'>
#hideCount > span { border-bottom: 1px dotted currentColor; }
#importGalleries, #exportGalleries { min-height: 420px; }
#settings-close {
	text-decoration: none;
	position: absolute;
	top: -2px;
	right: 1.5px;
	font-size: 1.4em;
}
#visControls { top: -6px; } `
+ (setStore.titleShow ? `div.gl4t:hover { overflow: visible; z-index: 3; position: relative; background: rgba(0, 0, 0, 0.5); height: auto; }` : ``) + `
ehx { font-family:` + $('body').css('font-family') + `, arial, symbola, SymbolaRegular;  }
table.itg > tbody > tr.ehx-visited, .gl1t.ehx-visited { ` + cssA.visible + ` }
input[type="checkbox"] {
	-webkit-appearance: none;
	border: 1px solid #F1F1F1BB;
	padding: 5px;
	top: 4px;
	background-color: transparent;
}
input[type="checkbox"]:checked:after {
	content: '\\2714';
	position: absolute;
	top: -8px;
	left: 1px;
	font-size: 1.1em;
}
input[type="checkbox"]:focus { outline: none; }
input[type="checkbox"]:hover { cursor: pointer; }
nav > div {
	text-align: right;
	margin-right: 30px;
}
nav > div button {
	border: none !important;
	padding: 1px 20px 1px 10px !important;
	position: relative;
}
table.itg > tbody > tr.ehx-visited.ehx-hidden, .gl1t.ehx-visited.ehx-hidden { ` + cssA.hidden + ` }
.active:after { content: '\\2212' !important; }
.applyContainer {
	padding-top: 5px;
	padding-right: 8px;
	border-top: 1px solid threedface;
	width: 100%;
	position: relative;
	left: -4px;
}
.branch {
	position: absolute;
	left: 10px;
	top: 1px;
	margin-left: -3px;
}
.collapsible {
	cursor: pointer;
	width: 100%;
	border: 0;
	outline: none;
	text-align: left;
	font-size: 1.25em;
	background-color: rgba(0, 0, 0, 0);
	color: inherit;
	font-weight: bold;
	padding:5px 3px;
	position: relative;
}
.collapsible:after {
	content: '\\002B';
	font-weight:bold;
	float:right;
	margin-right:5px;
}
.collapsible:before {
	content: '';
	position: absolute;
	padding: 4px;
	border-bottom: 1px solid threedface;
	border-left: 1px solid threedface;
	top: 5px;
	left: -11px;
}
.collapsible:hover { background-color: rgba(255, 255, 255, 0.1); }
.content {
	max-height: 0;
	overflow: hidden;
	transition: all .2s ease-in-out;
	border-bottom: 1px solid threedface;
}
.content button {
	margin-top: 3px;
	margin-right: 10px;
}
.control {
	position: relative;
	float: right;
	right: -5px;
}
.dropdown {
	display: none;
	position: absolute;
	z-index: 999;
	min-width: 150px;
	padding: 2px;
	border-radius: 1px;
	box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
	right: 0px;
	border: 1px solid threedface;
	background: ` + $('.ido').css('background') + `;
	background: ` + $('.ido').css('backgroundColor') + `;
}
.dropdown a {
	display: block;
	text-decoration: none;
	padding-right:2px;
}
.dropdown a:hover { background: rgba(255,255,255,0.2); }
.ehx-compact {
	border-style: solid;
	border-width: 1px 0;
	text-align: center;
}
.ehx-controls {
	padding: 3px 1px;
	text-align: center;
	display: block;
}
.ehx-exported-data { display: block; }
.ehx-extended {
	width: 120px;
	position: absolute;
	left: 3px;
	top: 172px;
	text-align: center;
	font-size: 8pt;
	line-height: 1.5;
}
.ehx-extended-favs {
	padding: 3px 1px;
	display: block;
	line-height: 1.5;
}
.ehx-hidden { ` + cssD + cssA.hidden + ` }
.ehx-hidden[data-jqstyle*="f"] {` + cssA.filter + `}
.ehx-hidden[data-jqstyle*="p"] {` + cssA.page + `}
.ehx-hidden[data-jqstyle*="r"] {` + cssA.rating + `}
.ehx-minimal {
	border-left: 1px solid #6f6f6f4d;
}
.ehx-minimal-text {
	text-align: center;
	display: block;
}
.ehx-thumbnail {
	display: block;
	text-align: center;
	margin: 3px 0 5px;
	line-height: 12px;
}
.ehx-visited .gl3e { min-height: 206px; }
.ehx-visited .gl4e { min-height: 264px !important; }
.gl2c { width: 115px; }
.inactive { display: none; }
.mencon {
	display: inline-block;
	position: relative;
}
.menu:after {
	content: '\\2335';
	position: absolute;
	right: 5px;
	bottom: 1px;
}
.notice {
	position: fixed;
	width: 500px;
	height: 20px;
	top: 20px;
	left: 50%;
	transform: translate(-50%, 0px);
	background: rgba(70, 130, 180, 0.8);
	font-size: 1.2em;
	font-weight: bold;
	color: white;
	padding-top: 5px;
	border-radius: 8px;
	z-index: 999;
}
.overlay {
	background: rgba(0,0,0,0.5);
	display: -webkit-flex;
	display: flex;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	z-index: 100;
	font-size: 9pt;
}
.overlay button:not(.collapsible) {
	background-color: transparent;
	border-radius: 6px;
	border: 1px solid threedface;
	cursor: pointer;
	font-weight: bold;
	padding: 3px 20px;
	text-decoration: none;
	color: inherit;
	margin-left: 5px;
}
.overlay button:not(.collapsible):hover { background-color: rgba(255, 255, 255, 0.1); }
.overlay button:not(.collapsible):focus { outline: none; }
.sControls { top: -3px; }
.section-container {
	text-align: left;
	overflow: auto;
	margin-bottom: 5px;
	padding-bottom: 5px;
}
.section-container textarea:disabled, .section-container input:disabled, .section-container select:disabled {
	opacity: 0.6;
	-webkit-opacity: 0.6;
}
.section-container input[type="number"] {
	border: 1px solid #8d8d8d;
	margin-left:0px;
	text-align: center;
	width: 50px;
}
.section-container select { margin-left: 0px; }
.section-container code {
	color: #000;
	background-color: #FFF;
}
.section-container fieldset { padding-right: 18px; }
.settings {
	background: ` + $('.ido').css('background') + `;
	background: ` + $('.ido').css('backgroundColor') + `;
	box-sizing: border-box;
	height: 555px;
	max-height: 100%;
	width: 900px;
	max-width: 100%;
	margin: auto;
	padding: 5px;
	display: -webkit-flex;
	display: flex;
	-webkit-flex-direction: column;
	flex-direction: column;
	box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.5);
}
.settings nav {
	text-align: right;
	padding-bottom: 5px;
	font-weight: bold;
	position: relative;
}
.settings legend {
	font-size: 10pt;
	font-weight: bold;
}
.settings label {
	font-weight: bold;
	text-decoration: underline;
	cursor: pointer;
}
.settings h3 {
	margin: 3px;
	position: relative;
}
.settings input { vertical-align: -1px; }
.settings textarea {
	width: 100%;
	height: 50px;
	resize: vertical;
}
.show { display:block }
.suboptions { position: relative; }
.suboptions > div {
	position: relative;
	padding-left: 1.4em;
}
.suboptions2 {
	margin-left: 4px;
	padding-left: 10px;
	margin-right: -9px;
}
</style>`).appendTo('head');
})();