EhxVisited

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

Fra 06.07.2020. Se den seneste versjonen.

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.56.10.8
// @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 = localStorage.getItem('ehx-settings') ? JSON.parse(localStorage.getItem('ehx-settings')) : {"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 = '';
    var cssA = localStorage.getItem('ehx-css') ? JSON.parse(localStorage.getItem('ehx-css')) : {"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;", "download": "box-shadow: inset 0 0 0 500px rgba(30, 180, 60, .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;", "uploader": "box-shadow: inset 0 0 0 500px rgba(222, 184, 135, .2) !important;"};
    var cssD = (setStore.softHide) ? 'opacity:0.2; -webkit-opacity: 0.2;' : 'display: none;';
	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==';
    /*════════════════════════════*/

    try { // Converting the old format to the new format // This can probably be removed now since it was only for transitioning
        filters = localStorage.getItem('ehx-filters') ? JSON.parse(localStorage.getItem('ehx-filters')) : {"title": "#\\[Erocolor\\]", "uploader": "#xcaliber9999"}; // JSON.parse isn't a fan of unique characters
    } catch (e) {
        filters = localStorage.getItem('ehx-filters') ? {"title": localStorage.getItem('ehx-filters'), "uploader": "#xcaliber9999"} : {"title": "#\\[Erocolor\\]", "uploader": "#xcaliber9999"};
    }

    let db = null, $ = window.jQuery;
    var filterArr = [], uploaderArr = [];
    var observer = new MutationObserver(e => {
        addCSS();
    });

    var spl = document.URL.split('/');
    var d1 = spl[3];
    var galleries, hidden, down;
    var activeStore, activeStoreTitle;
    var ehxClearConfirm = 0, ehxhClearConfirm = 0, ehxdClearConfirm = 0, reload = 0, offset = 0;
	const category = {doujinshi: 'ct2', manga: 'ct3', artistcg: 'ct4', gamecg: 'ct5', western: 'cta', nonh: 'ct9', imageset: 'ct6', cosplay: 'ct7', asianporn: 'ct8', misc: 'ct1'};

    if (d1 == 'g') addGallery('galleries', spl[4] + '.' + spl[5]); // Add the current page to galleries
    if (d1.startsWith('gallerytorrents')) $('#torrentinfo a').click(e => addGallery('down', getUrlVars().gid + '.' + getUrlVars().t));
	if (d1.startsWith('archiver')) {
		$(':submit').click(e => addGallery('down', getUrlVars().gid + '.' + getUrlVars().token));
		$('a').click(e => addGallery('down', getUrlVars().gid + '.' + getUrlVars().token));
	}
    if (d1.substr(0, 1) == '?' || d1.substr(0, 1) == 'w' || d1.substr(0, 1) == '#' || d1.substr(0, 1) == 'f' || d1.substr(0, 1) == 'p' || d1.substr(0, 1) == 't' || !d1) {
        $('<div class="alertContainer"></div>').appendTo('body');
        populate();
    }

    /**
     * Populate the custom filter array with user input converted into regular expressions
	 */
    function populateFilter() {
        filterArr = []; // Start fresh for when we call this again
        uploaderArr = [];
        if (filters.title != '') pushRegex('title', filterArr);
        if (filters.uploader != '') pushRegex('uploader', uploaderArr);

		function pushRegex(store, arr) {
			var tempArr = filters[store].split('\n');
            for (var i = 0; i < tempArr.length; i++) {
                if (tempArr[i].startsWith('#')) continue;
                var regex;
                try {
                    regex = new RegExp(tempArr[i], 'i');
                } catch(e) {
                    displayAlert('Invalid Regex On Line ' + (i + 1), 5000, true);
                    continue;
                }
                arr.push(regex);
            }
		}
    }

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

        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'});
			if (!db.objectStoreNames.contains('down')) db.createObjectStore('down', {keyPath: 'id'});
        };

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

            var objStore = db.transaction(store, 'readwrite').objectStore(store);
            var openRequest = objStore.openCursor(gid);

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

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

    /**
     * Updates the hidden gallery count in the header object
	 */
    function updateGListing() {
        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 hCount = $('.ehx-hidden').length, vAmount = $('.ehx-hidden.ehx-visited').length;
		var hAmount = $('div[data-jqstyle*="h"]').length, fAmount = $('div[data-jqstyle*="f"]').length, pAmount = $('div[data-jqstyle*="p"]').length, rAmount = $('div[data-jqstyle*="r"]').length, uAmount = $('div[data-jqstyle*="u"]').length;
        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 + ' | Uploader: ' + uAmount);
		$('#hLength').text(Object.keys(hidden).length);
		$('#gLength').text(Object.keys(galleries).length);
    }

    /**
     * Convert the star count of a specified element to a double
	 * @param {Object} el - A specific element within the DOM
	 */
    function getStarNumber(el, transpose) {
		var starCount = {5: '0px -1px', 4.5: '0px -21px', 4: '-16px -1px', 3.5: '-16px -21px', 3: '-32px -1px', 2.5: '-32px -21px', 2: '-48px -1px', 1.5: '-48px -21px', 1: '-64px -1px', 0.5: '-64px -21px'};
		if (!transpose) {
			var stars = $(el).find('.ir').css('background-position');
			return Object.keys(starCount).find(key => starCount[key] === stars);
		} else return starCount[(Math.round(el * 2) / 2).toFixed(1)]; // Ratings are given in x.xx numbers, but we need either whole integers, or half integers
    }

    /**
	 * 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) { // TODO: See about sorting out this clusterfuck
        if (filterArr.length) {
            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');
                if (!($(el).attr('data-jqstyle') || '').match('f')) addStyle($(el), 'f');
            } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'f');
        } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'f');

        var upload = ''; // Uploader name is stored in one of two elements based on view modes, not displayed in Thumbnail view
        if ($('.gl1e').length) upload = $(el).find('.gl3e > div:nth-child(4)').text();
        else if ($('.gl1c').length || $('.gl1m').length) upload = $(el).find('.glhide > div a').text();

        if (uploaderArr.length) {
            if (uploaderArr.some(rx => rx.test(upload))) {
                if (!$(el).hasClass('ehx-hidden')) $(el).addClass('ehx-hidden');
                if (!($(el).attr('data-jqstyle') || '').match('u')) addStyle($(el), 'u');
            } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'u');
        } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'u');

        // Filter our galleries through the star limit filter
        if (setStore.stFilter && setStore.stLimit > 0) {
            if (getStarNumber(el, false) < setStore.stLimit) {
                if (!$(el).hasClass('ehx-hidden')) $(el).addClass('ehx-hidden');
                if (!($(el).attr('data-jqstyle') || '').match('r')) addStyle($(el), 'r');
            } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'r');
        } else if ($(el).hasClass('ehx-hidden')) removeStyle($(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');
                if (!($(el).attr('data-jqstyle') || '').match('p')) addStyle($(el), 'p');
            } else if ($(el).hasClass('ehx-hidden')) removeStyle($(el), 'p');
        } else if ($(el).hasClass('ehx-hidden')) removeStyle($(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')) $(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')) {
            $(el).attr('data-jqstyle', $(el).attr('data-jqstyle').replace(flag, ''));
            if (!$(el).attr('data-jqstyle')) $(el).removeClass('ehx-hidden'); // Replacing the flag brought jqstyle to blank
        } else $(el).removeClass('ehx-hidden');
    }

    /**
     * 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', 2);
        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).css('display', '');
                    $(el).removeClass('ehx-hidden');
                    removeStyle($(el), 'h');
                    delete hidden[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');
                    if ($('#ehxh-show').text() === 'Hide') $(el).css('display', $('.gl1t').length ? 'flex' : 'table-row');
                    addStyle($(el), 'h');
                    hidden[tgid] = 1; // Add gallery listing to our local store of hidden galleries
                }
                updateGListing();
            }
        }
    }

	/**
	 * Deletes the item from the history tab and database
	 * TODO: Combine this and toggleElement into a more generic function
	 * @param {String} href - URL of the gallery
	 * @param {HTML Element} el - The parent element to apply CSS to
	 */
	function deleteHistory(href, el) {
		const request = indexedDB.open('ehxvisited', 2);
		var tgid = href.split('/')[4] + '.' + href.split('/')[5];

		request.onsuccess = e => {
			db = e.target.result;
			var objStore = db.transaction(activeStoreTitle, 'readwrite').objectStore(activeStoreTitle);
			var openReq = objStore.delete(tgid);
			openReq.onsuccess = e => {
				console.log('EhxVisited: Removed ' + tgid + ' from ' + activeStoreTitle + ' DB.');
				delete activeStore[tgid];
				displayAlert("Removed " + tgid + " from the database", 5000, false);
				$(el).addClass('removed');
				updateGListing();
			}
		}
        delete activeStore[tgid];
        addCSS();
	}

	/**
	 * Sorts a dictionary based off the value of a key
	 * @param {Dictionary} obj - A dictionary
	 * @return {Dictionary} sorted_obj - A sorted dictionary
	 */
    function sortObj(obj) {
        var items = Object.keys(obj).map(k => [k, obj[k]]); // Convert Dictionary into an array of arrays
        items.sort((a, b) => b[1] - a[1]); // Sort by value of original key value pair
        var sorted_obj = {};
        $.each(items, (k, v) => {
            sorted_obj[v[0]] = v[1] // Put the items back into Dictionary form
        })
        return (sorted_obj);
    }

	/**
	 * Returns an object with the current URL parameters
	 */
	function getUrlVars() {
		var vars = {};
		var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
			vars[key] = value;
		});
		return vars;
	}

    /**
	 * 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
		populateFilter();
		galleries = [];
		hidden = [];
		down = [];
        const request = indexedDB.open('ehxvisited', 2);

        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'});
			if (!db.objectStoreNames.contains('down')) db.createObjectStore('down', {keyPath: 'id'});
        };

        request.onsuccess = e => { // TODO: See about multiple transactions, or just do these async
            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[transform[i].id] = transform[i].visited; // Force matrix data into array data
                }
                var gLength = Object.keys(galleries).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[transform2[i].id] = 1; // Force matrix data into array data
                    }
                    galleries = sortObj(galleries);
                    var hLength = Object.keys(hidden).length
					var objStore3 = db.transaction('down', 'readonly').objectStore('down');
					var openReq3 = objStore3.getAll();
					openReq3.onsuccess = h => {
						var transform3 = h.target.result;
						for (i = 0; i < transform3.length; i++) {
							down[transform3[i].id] = 1;
						}
						$($('h1').text() == 'Favorites' ? '.ido > div:nth-child(3)' : '#toppane').append(
							`<ehx id="ehx-controls">Galleries visited: <span id="gLength">` + gLength + `</span> ( <span id="ehx-menu-control"></span><a id="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 id="ehx-show">' + ((setStore.visHide) ? 'Show' : 'Hide') + '</a> / ');
							$('#ehxh-menu-control').append(' ( <a id="ehxh-show">' + ((setStore.hidShow) ? 'Hide' : 'Show') + '</a> )');
						}
						$('#ehx-settings').click(e => {
							e.preventDefault();
							settings();
						});
						$('#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').text() === '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) { // 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').text() === '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: Probably refactor this block again
		observer.disconnect();
		var list = $('.itg tr').length ? $('tr').has('.glhide, .gldown, th') : $('.itg .gl1t');
		var gid, galleryId, onFavs;

		if (list.length) {
			if ($('h1').text() == 'Favorites') onFavs = 1;
			if ($('.gl1e').length) { // Extended
				for (var i = 0; i < list.length; i++) {
					gid = $(list[i]).find('.gl1e a').attr('href').split('/');
					galleryId = gid[4] + '.' + gid[5];

					if (galleries[galleryId] != undefined) { // Visited
                        if (!$(list[i]).hasClass('ehx-visited')) { // Append our fields if we haven't already
                            $(list[i]).addClass('ehx-visited');
							addStyle($(list[i]), 'v');
                            if (onFavs) {
                                $(list[i]).find('.gl3e div:last-child').append('<br/><ehx class="ehx-extended-favs">\uD83D\uDC41 ' + timeDifference(galleryId) +'<br>' + buildTime(galleryId, false) + '</ehx>');
                            } else {
                                $(list[i]).find('.gl3e').append('<ehx class="ehx-extended">\uD83D\uDC41 ' + timeDifference(galleryId) +'<br>' + buildTime(galleryId, false) + '</ehx>');
                            }
                        } else { // Otherwise, just update the timestamp
                            if (onFavs) {
                                $(list[i]).find('ehx-extended-favs').text('\uD83D\uDC41 ' + timeDifference(galleryId) +'<br>' + buildTime(galleryId, false));
                            } else {
                                $(list[i]).find('ehx-extended').text('\uD83D\uDC41 ' + timeDifference(galleryId) +'<br>' + buildTime(galleryId, false));
                            }
                        }

                        if (setStore.cssTT) $(list[i]).find('.glname').attr('title', '\uD83D\uDC41 ' + buildTime(galleryId, true));
                        if (setStore.repPub) $(list[i]).find('.gl3e div:nth-child(2)').text(buildTime(galleryId, false));
					} else { // Never Visited
						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');
                        $(list[i]).removeClass('ehx-visited');
                        if ($(list[i]).find('.ehx-extended').length) $(list[i]).find('.ehx-extended').remove();
					}

					if (hidden[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
						$(list[i]).addClass('ehx-hidden');
						addStyle($(list[i]), 'h');
					}

					if (down[galleryId] != undefined && !$(list[i]).hasClass('ehx-downloaded')) {
						$(list[i]).addClass('ehx-downloaded');
						addStyle($(list[i]), 'd');
					}

					if (!$(list[i]).find('.imgHide').length) {
						$('<img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery">').appendTo($(list[i]).find('.gl2e > div')).click(e => { // Maybe closest('tr')
							toggleElement($(e.currentTarget).parents().eq(2).find('a').attr('href'), $(e.currentTarget).parents().eq(2));
						});
					}
					filterCheck($(list[i]));
				}
			} else if ($('.gl1c').length) { // Compact
				var borderColor = $('.gl1c').first().css('border-top-color');
				if ($('.itg tr:first-child').children().length < 5) { // We haven't appended our table head
					$('.itg th:nth-child(4)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">&#x2716</th>'); // X column
					if (setStore.minAdd) $('.itg th:nth-child(2)').after('<th>Visited</th>');
					if (setStore.repPub) $('.itg th:nth-child(2)').text('Visited');
				}

				for (i = 1; i < list.length; i++) {
					gid = $(list[i]).find('.glname a').attr('href').split('/');
					galleryId = gid[4] + '.' + gid[5];

					if (galleries[galleryId] != undefined) { // Visited
                        if (!$(list[i]).hasClass('ehx-visited')) { // Append our fields
                            $(list[i]).addClass('ehx-visited');
							addStyle($(list[i]), 'v');
                            if (setStore.minAdd) {
                                if ($(list[i]).find('.ehx-compact').length) $(list[i]).find('ehx-compact').html('<ehx>' + timeDifference(galleryId, true) + '<br>' + buildTime(galleryId, false).substring(11) + '<br>' + buildTime(galleryId, false).substring(2, 10) + '</ehx>');
                                else $(list[i]).find('.gl2c').after('<td class="ehx-compact" style="border-color:' + borderColor + ';"><ehx>' + timeDifference(galleryId, true) + '<br>' + buildTime(galleryId, false).substring(11) + '<br>' + buildTime(galleryId, false).substring(2, 10) + '</ehx></td>');
                            }
                        } else { // Otherwise update timestamp
                            $(list[i]).find('ehx-compact').html('<ehx>' + timeDifference(galleryId, true) + '<br>' + buildTime(galleryId, false).substring(11) + '<br>' + buildTime(galleryId, false).substring(2, 10) + '</ehx>');
                        }

                        if (setStore.cssTT) $(list[i]).find('.glname').attr('title', '\uD83D\uDC41 ' + buildTime(galleryId, true));
                        if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3) div:first-child').text(buildTime(galleryId, false));
					} else { // Never Visited
						if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited');
						if (setStore.repPub) $(list[i]).find('.gl2c > div:nth-child(3) > div:first-child').text('Never Visited');
						if ($(list[i]).children().length < 5 || ($(list[i]).children().length < 6 && onFavs)) {
							if (setStore.minAdd) $(list[i]).find('.gl2c').after('<td class="ehx-compact" style="border-color:' + borderColor + ';"></td>');
						}
                        $(list[i]).removeClass('ehx-visited');
					}

					if (hidden[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
                        $(list[i]).addClass('ehx-hidden');
                        addStyle($(list[i]), 'h');
					}

					if (down[galleryId] != undefined && !$(list[i]).hasClass('ehx-downloaded')) {
						$(list[i]).addClass('ehx-downloaded');
						addStyle($(list[i]), 'd');
					}

					if (!$(list[i]).find('.imgHide').length) {
						$('<td class="hideContainer"><img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery"></td>').appendTo($(list[i]).closest('tr')).click(e => {
							toggleElement($(e.currentTarget).parent().find('.glname a').attr('href'), $(e.currentTarget).parent());
						});
					}
					filterCheck($(list[i]));
				}
			} else if ($('.gl1m').length) { // Minimal
				if ($('.itg tr:first-child').children().length < 7) { // We haven't appended our table head
					$('.itg th:nth-child(6)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">&#x2716</th>'); // X Column
					if (setStore.minAdd) $('.itg th:nth-child(2)').after('<th title="EhxVisited: Hover for timestamps" style="text-align: center;">\uD83D\uDC41</th>');
					if (setStore.repPub) $('.itg th:nth-child(2)').text('Visited');
				}

				for (i = 1; i < list.length; i++) {
					gid = $(list[i]).find('.glname a').attr('href').split('/');
					galleryId = gid[4] + '.' + gid[5];

					if (galleries[galleryId] != undefined) { // Visited
                        if (!$(list[i]).hasClass('ehx-visited')) { // Append fields
                            $(list[i]).addClass('ehx-visited');
							addStyle($(list[i]), 'v');
                            if (setStore.minAdd) {
                                if (setStore.minShow) {
                                    if ($(list[i]).find('.ehx-minimal').length) $(list[i]).find('.ehx-minimal').html('<ehx title="' + buildTime(galleryId, false) +'">' + timeDifference(galleryId, true) + '</ehx>');
                                    else $(list[i]).find('.gl2m').after('<td class="ehx-minimal"><ehx title="EhxVisited: ' + buildTime(galleryId, false) +'">' + timeDifference(galleryId, true) + '</ehx></td>');
                                } else {
                                   if ($(list[i]).find('.ehx-minimal').length) {
                                        $(list[i]).find('.ehx-minimal').html('<ehx>\uD83D\uDC41</ehx>');
                                        $(list[i]).find('.ehx-minimal').attr('title', 'EhxVisited: ' + buildTime(galleryId, true));
                                    } else $(list[i]).find('.gl2m').after('<td class="ehx-minimal" title="EhxVisited: ' + buildTime(galleryId, true) + '"><ehx>\uD83D\uDC41</ehx></td>');
                                }
                            }
                        } else { // Update our timestamps
                            if (setStore.minAdd) {
                                if (setStore.minShow) {
                                    $(list[i]).find('.ehx-minimal').html('<ehx title="' + buildTime(galleryId, false) +'">' + timeDifference(galleryId, true) + '</ehx>');
                                } else {
                                    $(list[i]).find('.ehx-minimal').html('<ehx>\uD83D\uDC41</ehx>');
                                    $(list[i]).find('.ehx-minimal').attr('title', 'EhxVisited: ' + buildTime(galleryId, true));
                                }
                            }
                        }

                        if (setStore.cssTT) $(list[i]).find('.glname a').attr('title', '\uD83D\uDC41 ' + buildTime(galleryId, true));
                        if (setStore.repPub) $(list[i]).find('.gl2m div:nth-child(3)').text(buildTime(galleryId, false));
					} else { // Never Visited
						if (setStore.cssTT) $(list[i]).find('.glname a').attr('title', 'Never Visited');
						if (setStore.repPub) $(list[i]).find('.gl2m div:nth-child(3)').text('Never Visited');
                        if ($(list[i]).children().length < 7 || ($(list[i]).children().length < 8 && onFavs)) {
						    if (setStore.minAdd) $(list[i]).find('.gl2m').after('<td class="ehx-minimal"></td>');
                        }
                        $(list[i]).removeClass('ehx-visited');
					}

					if (hidden[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
						$(list[i]).addClass('ehx-hidden');
						addStyle($(list[i]), 'h');
					}

					if (down[galleryId] != undefined && !$(list[i]).hasClass('ehx-downloaded')) {
						$(list[i]).addClass('ehx-downloaded');
						addStyle($(list[i]), 'd');
					}

					if (!$(list[i]).find('.imgHide').length) {
						$('<td class="hideContainer"><img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery"></td>').appendTo($(list[i]).closest('tr')).click(e => {
							var el = $(e.currentTarget).closest('tr');
							toggleElement($(el).find('.glname a').attr('href'), $(el));
						});
					}
                    filterCheck($(list[i]));
				}
			} else { // Thumbnail
				for (i = 0; i < list.length; i++) {
					gid = $(list[i]).find('.gl3t a').attr('href').split('/');
					galleryId = gid[4] + '.' + gid[5];

					if (galleries[galleryId] != undefined) { // Visited
                        if (!$(list[i]).hasClass('ehx-visited')) {
							$(list[i]).addClass('ehx-visited');
							addStyle($(list[i]), 'v');
							if (setStore.titleShow) $(list[i]).find('.gl5t').append('<div style="position: absolute; top: 45px;"><ehx class="ehx-thumbnail">\uD83D\uDC41 ' + buildTime(galleryId, true) + '</ehx></div>');
                            else $(list[i]).find('.gl5t').after('<ehx class="ehx-thumbnail">\uD83D\uDC41 ' + buildTime(galleryId, true) + '</ehx>');
                        } else {
                            $(list[i]).find('.ehx-thumbnail').text('\uD83D\uDC41 ' + buildTime(galleryId, true));
                        }

                        if (setStore.cssTT) $(list[i]).find('.glname').attr('title', '\uD83D\uDC41 ' + buildTime(galleryId, true));
                        if (setStore.repPub) $(list[i]).find('.gl5t div:first-child div:nth-child(2)').text(buildTime(galleryId, false));
					} else { // Never Visited
						if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited');
                        if (setStore.repPub) $(list[i]).find('.gl5t div:first-child div:nth-child(2)').text('Never Visited');
                        $(list[i]).removeClass('ehx-visited');
                        if ($(list[i]).find('.ehx-thumbnail').length) $(list[i]).find('.ehx-thumbnail').parentElement.remove();
					}

					if (hidden[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
						$(list[i]).addClass('ehx-hidden');
						addStyle($(list[i]), 'h');
					}

					if (down[galleryId] != undefined && !$(list[i]).hasClass('ehx-downloaded')) {
						$(list[i]).addClass('ehx-downloaded');
						addStyle($(list[i]), 'd');
					}

					if (!$(list[i]).find('.imgHide').length) {
						$('<div class="hideContainer"><img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery"></div>').appendTo($(list[i]).find('.gl5t')).on('click', e => {
							var el = $(e.currentTarget).parents().eq(1);
							toggleElement($(el).find('.gl3t a').attr('href'), $(el));
						});
					}
					filterCheck($(list[i]));
				}
			}
			updateGListing();
		} else { // No Elements pulled, invalid view
			displayAlert('No Valid Elements Detected', 5000, true);
			return;
		}

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

	/**
     * Build the time difference string
     * @param {String} gid - Gallery ID
	 * @param {Boolean} time - Include timeDifference in returned string
	 * @param {Boolean} abbrv - Abbreviate for timeDifference
     */
    function buildTime(gid, time, abbrv) {
        var d = new Date(galleries[gid]);
		var str = d.getFullYear().toString() + '-' + (d.getMonth() + 1).toString().padStart(2, '0') + '-' + d.getDate().toString().padStart(2, '0') + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0');
		if (time) return timeDifference(gid, abbrv) + ' ' + str;
		return str;
    }

    /**
	 * 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(gallery, abbreviate) {
		var previous = galleries[gallery];
        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
	 * @param {Boolean} error - Is this an error message
	 */
	function displayAlert(message, timeout, error) {
		var alert = $('<div class="notice ' + ((error) ? 'alert' : '') + '">EhxVisited: ' + message + '</div>');
		$(alert).hide().appendTo('.alertContainer').fadeIn(1000);
		setTimeout(e => { $('.notice').fadeOut(1000, f => { $('.notice').remove(); }); }, timeout);
	}

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

    /**
     * Generate the JSON request for the E-H API
     * @param {IndexedDB Keys} data - Object keys within the data portion of our matrices
     */
    function generateRequest(data) {
		$('#listingContainer').empty();
        var reqList = []; // We use an array for our gidlist, since the API can handle up to 25 galleries per request
        for (var i = 0; i < data.length; i++) {
			if (data[i] == undefined) continue;
            var str = data[i].split('.'); // Split the key to match request specifications of galleryID, galleryToken
            reqList[i] = [str[0], str[1]];
        }
        var request = {"method": "gdata", "gidlist": reqList, "namespace": 1};

        var req = new XMLHttpRequest();
        req.onreadystatechange = e => {
            if (req.readyState == 4) {
				if (req.status == 200) {
					var apirsp = JSON.parse(req.responseText);
					//console.log(apirsp);
					for (var i = 0; i < apirsp.gmetadata.length; i++) generateListing(apirsp.gmetadata[i]);
				} else {
					console.error();
					displayAlert("Request Failed", 5000, true);
				}
			}
        }
        req.open("POST", document.location.origin + "/api.php", true); // Due to CORS, we need to use the API on the same domain as the script
        req.send(JSON.stringify(request));
    }

	/**
	 * Generate the HTML code for each individual listing in history views
	 * @param {JSON Array} glisting - E-H API response item for a specified gallery
	 */
	function generateListing(glisting) {
		var d = new Date(glisting.posted * 1000);
		// TODO: See about replacing the custom date with a buildTime call
		var listing = $(`
		<div class="listing">
			<div class="thumb">
				<a href="` + document.location.origin + '/g/' + glisting.gid + '/' + glisting.token + `">
					<img src="` + glisting.thumb + `" />
				</a>
			</div>
			<div class="listBody">
				<div class="title" style="width: 90%">
					<a href="` + document.location.origin + '/g/' + glisting.gid + '/' + glisting.token + '/">' + glisting.title + `</a>
				</div>
				<div class="category">
					<div class="cn ` + category[glisting.category.toLowerCase().replace(/ /g, '').replace(/-/g, '')] + `">
						<a href="` + document.location.origin + '/' + glisting.category.toLowerCase().replace(/ /g, '') + '">' + glisting.category + `</a>
					</div>
					<div class="date">
						` + d.getFullYear().toString() + '-' + (d.getMonth() + 1).toString().padStart(2, '0') + '-' + d.getDate().toString().padStart(2, '0') + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + `
					</div>
				</div>
				<div class="rating">
					<div>
						<a href="` + document.location.origin + '/uploader/' + glisting.uploader + '">' + glisting.uploader + `</a>
					</div>
					<div>
						` + glisting.filecount + ` pages
					</div>
					<div class="ir" style="float: right; background-position: ` + getStarNumber(glisting.rating, true) + `"></div>
				</div>
			</div>
		</div>`);
		$('#listingContainer').append(listing);
		$('<div class="imgContainer"><img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery"></div>').appendTo($('.listBody').last()).on('click', e => {
			deleteHistory($(e.currentTarget).parents().eq(1).find('a').attr('href'), $(e.currentTarget).parents().eq(1));
		});
	}

    /**
	 * Import user data into our indexedDB
	 * @param {String} items - String of exported data to import
	 */
    function ehxImport(items) {
		const req = indexedDB.open('ehxvisited', 2);
			req.onsuccess = e => {
				if (db == null) db = e.target.result;
			var objStore = db.transaction(activeStoreTitle, 'readwrite').objectStore(activeStoreTitle);
			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(':');
					objStore.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
					activeStore[str[0]] = str[1];
					++count;
				} else {
					displayAlert('Imported ' + count + ' entries', 5000);
					console.log('EhxVisited: Merge Completed');
					updateGListing();
					addCSS();
				}
			}
		}
    }

	/**
	 * Fills a text area with formatted gallery data for export
	 * @param {IndexedDB Title} store - The name of an indexedDB store
	 */
	function ehxExport(store) {
		const req = indexedDB.open('ehxvisited', 2);
		req.onsuccess = e => {
			if (db == null) db = e.target.result;
			var objStore = db.transaction(store, 'readonly').objectStore(store);
			var openReq = objStore.getAll();
			openReq.onsuccess = e => {
				var data = '';
				for (var i in e.target.result) {
					data += e.target.result[i].id + ':' + e.target.result[i].visited + ';';
				}
				$('#exportGalleries').val(data); // Fill with formatted data
			}
		}
	}

    /**
     * Remove our stylesheet with transient CSS, and then re-add it with the updated CSS
     */
    function updateCSS() {
		cssD = (setStore.softHide) ? 'opacity:0.2; -webkit-opacity: 0.2;' : 'display: none;';
        $('#setStyle').remove();
        $(`<style id="setStyle" data-jqstyle="ehxVisited">
        table.itg > tbody > .ehx-visited, .ehx-visited { ` + cssA.visible + ` }
        table.itg > tbody > .ehx-visited.ehx-hidden, .ehx-visited.ehx-hidden { ` + cssA.hidden + ` }
        .ehx-hidden { ` + cssD + cssA.hidden + ` }
		.ehx-downloaded { ` + cssA.download + ` }
        .ehx-hidden[data-jqstyle*="f"] {` + cssA.filter + `}
        .ehx-hidden[data-jqstyle*="p"] {` + cssA.page + `}
        .ehx-hidden[data-jqstyle*="r"] {` + cssA.rating + `}
        .ehx-hidden[data-jqstyle*="u"] {` + cssA.uploader + `}
        </style>`).appendTo('head');
    }

    /**
	 * Open the Settings menu and set up all necessary menu functions
	 */
    function settings() {
		// 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 id="topNav">
					<button id="home" style="float: left; border: none;">Main</button>
					<span id="setNotice" style="width: 100%; margin-left: 8px; margin-top: 2px; font-weight: lighter; opacity: 0.5; -webkit-opacity: 0.5; text-align: center; position: absolute; left: 0;">` + (reload ? `Applied Settings Will Take Effect On Reload` : ``) + `</span>
					<div>
						<div class="mencon">
							<button class="menu">Export</button>
							<div class="dropdown">
								<a id="ehx-export">Export Galleries</a>
								<a id="ehxh-export">Export Hidden Galleries</a>
								<a id="ehxd-export">Export DL Galleries</a>
							</div>
						</div>
						<div class="mencon">
							<button class="menu">Import</button>
							<div class="dropdown">
								<a id="ehx-import">Import Galleries</a>
								<a id="ehxh-import">Import Hidden Galleries</a>
								<a id="ehxd-import">Import DL Galleries</a>
							</div>
						</div>
						<a id="settings-close">&#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="visHistory">View History</button>
									<button id="resV">Reset CSS</button>
									<button id="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 hideControls">
									<button id="hidHistory">View</button>
									<button id="resH">Reset CSS</button>
									<button id="ehxh-clear">Clear Data</button>
								</div>
							</h3>
							<textarea id="hidden" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.hidden + `</textarea>
							<h3>Downloaded Galleries
								<div class="control hideControls">
									<button id="dowHistory">View</button>
									<button id="resD">Reset CSS</button>
									<button id="ehxd-clear">Clear Data</button>
								</div>
							</h3>
							<textarea id="downloaded" class="field" spellcheck="false">` + cssA.download + `</textarea>
							<div class="suboptions2">
								<button class="collapsible">Title 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">Uploader Filtered Galleries</button>
								<div class="content">
									<textarea id="ufiltered" class="field" spellcheck="false">` + cssA.uploader + `</textarea>
									<div class="control sControls">
										<button id="resU">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>\\[Sample\\]</code> will filter out every gallery with <code>[Sample]</code>, case-insensitive, in the title.</li>
								<li>Lines starting with <code>#</code> will be ignored.</li>
							</ul>
							<textarea id="galFilter">` + filters.title + `</textarea>
							<h3>Uploader Filter<span style="font-weight: lighter; font-size: .8em; opacity: 0.5; -webkit-opacity: 0.5; margin-left: 8px;">(Doesn't Apply To Thumbnail View)</span></h3>
							<textarea id="upFilter">` + filters.uploader + `</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;">
							<legend id="importTitle">Import Galleries</legend>
							<textarea id="importGalleries"></textarea>
							<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
								<button class="close">Close</button>
								<button id="importConfirm">Import</button>
							</div>
						</fieldset>
					</section>
					<section class="inactive">
						<fieldset style="padding-bottom: 2px;">
							<legend id="exportTitle">Export Galleries</legend>
							<textarea id="exportGalleries"></textarea>
							<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
								<button class="close">Close</button>
								<button id="exportCopy">Copy</button>
							</div>
						</fieldset>
					</section>
					<section class="inactive">
						<fieldset>
							<legend id="history" style="margin-left: 5px;"></legend>
							<div id="listingContainer">
							</div>
						</fieldset>
					</section>
				</div>
				<div class="applyContainer">
					<div class="control" id="applyCon" style="padding-right: 5px;">
						<button id="apply">Apply</button>
					</div>
				</div>
			</div>
		</div>`);
		$('body').append(container);
		$('body').addClass('noscroll');
		galleries = sortObj(galleries);
		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;'); });
		$('#resD').click(e => { $('#downloaded').val('box-shadow: inset 0 0 0 500px rgba(30, 180, 60, .2) !important;'); });
		$('#resF').click(e => { $('#filtered').val('box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;'); });
		$('#resU').click(e => { $('#ufiltered').val('box-shadow: inset 0 0 0 500px rgba(222, 184, 135, .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);

			if ($('#minAdd').is(e.target) || $('#minShow').is(e.target) || $('#repPub').is(e.target) || $('#titleShow').is(e.target) || $('#cssTT').is(e.target)) {
				$('#setNotice').text('Applied Settings Will Take Effect On Reload');
				reload = 1;
			}
		});
		$('#settings-close').click(e => {
			$('.overlay').remove();
			$('body').removeClass('noscroll');
		});
		$('body').click(e => {
			if (e.target.className == "overlay") { // Exit if settings menu isn't clicked
				$('.overlay').remove();
			} else if (e.target.className != 'show' && e.target.className != 'menu') {
				$('.show').removeClass('show');
			}
			if (!$('.overlay').length) $('body').removeClass('noscroll');
		});
		$('#apply').click(e => applySettings());

		/**
		 * Parse our HTML options into a temporary JSON array and then stringify it into localStorage
		 */
		function applySettings() { // TODO: Sort this shit out into one block
			setStore = { // Store this independantly, so it doesn't mess up table appends
				"softHide": $('#softHide').prop('checked'),
				"minAdd": setStore.minAdd,
				"minShow": setStore.minShow,
				"cssTT": $('#cssTT').prop('checked'),
				"repPub": setStore.repPub,
				"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": setStore.titleShow
			}
			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(),
				"download": $('#downloaded').val(),
				"filter":  $('#filtered').val(),
				"page": $('#page').val(),
				"rating": $('#rating').val(),
				"uploader": $('#ufiltered').val()
			}
			cssA = tempCss;
			localStorage.setItem('ehx-css', JSON.stringify(tempCss));
			var tempFilt = { // Remove null entries because bad things happen if they're there
				"title": $('#galFilter').val().replace(/^\s*[\r\n]/gm, ''),
				"uploader": $('#upFilter').val().replace(/^\s*[\r\n]/gm, '')
			}
			filters = tempFilt;
			populateFilter();
			localStorage.setItem('ehx-filters', JSON.stringify(tempFilt));
			updateCSS();
			addCSS();
			displayAlert('Applied Current Settings', 5000, false);
		}

		$('.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';
		});

		function swapContainer(index) {
			$('.section-container section').addClass('inactive');
			$('.section-container section:nth-child(' + index + ')').removeClass('inactive');
			if (index == 2) $('#importGalleries').val('');
		}

		$('#ehx-import').click(e => {
			swapContainer(2);
			activeStore = galleries;
			activeStoreTitle = 'galleries';
			$('#importTitle').text('Import Galleries');
		});
		$('#ehxh-import').click(e => {
			swapContainer(2);
			activeStore = hidden;
			activeStoreTitle = 'hidden';
			$('#importTitle').text('Import Hidden Galleries');
		});
		$('#ehxd-import').click(e => {
			swapContainer(2);
			activeStore = down;
			activeStoreTitle = 'down';
			$('#importTitle').text('Import Downloaded Galleries');
		});
		$('#importConfirm').click(e => ehxImport($('#importGalleries').val().replace(/^\s*[\r\n]/gm, '')));

		$('.close').click(e => {
			$('.section-container section').addClass('inactive');
			$('.section-container section:first-child').removeClass('inactive');
		});
		$('#home').click(e => {
			$('.section-container section').addClass('inactive');
			$('.section-container section:first-child').removeClass('inactive');
			$('#pages').remove();
			if (!$('#apply').length) $('.applyContainer').append($('<div class="control" id="applyCon" style="padding-right: 5px;"><button id="apply">Apply</button></div>'));
		});

		$('#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('galleries');
		});
		$('#ehxh-export').click(e => {
			$('.section-container section').addClass('inactive');
			$('.section-container section:nth-child(3)').removeClass('inactive');
			$('#exportTitle').text('Exported Hidden Galleries');
			ehxExport('hidden');
		});
		$('#ehxd-export').click(e => {
			$('.section-container section').addClass('inactive');
			$('.section-container section:nth-child(3)').removeClass('inactive');
			$('#exportTitle').text('Exported Downloaded Galleries');
			ehxExport('down');
		});

		$('#visHistory').click(e => {
			activeStore = galleries;
			activeStoreTitle = 'galleries';
			generateHistory('Viewed Galleries');
		});
		$('#hidHistory').click(e => {
			activeStore = hidden;
			activeStoreTitle = 'hidden';
			generateHistory('Hidden Galleries');
		});
		$('#dowHistory').click(e => {
			activeStore = down;
			activeStoreTitle = 'down';
			generateHistory('Downloaded Galleries');
		});

		function generateHistory(text) {
			$('#history').text(text);
			$('#applyCon').remove();

			var pageSelect = '<div id="pages">Page <select id="pageCount">';
			for (var i = 0; i < Math.ceil(Object.keys(activeStore).length / 25); i++) {
				pageSelect += '<option>' + (i + 1) + '</option>';
			}

			pageSelect += '</select> of ' + Math.ceil(Object.keys(activeStore).length / 25) + ' pages</div>';
			$('.section-container section').addClass('inactive');
			$('.section-container section:nth-child(4)').removeClass('inactive');
			$('.applyContainer').append($.parseHTML(pageSelect));

			var str = [];
			for (i = 0; i < 25; i++) str[i] = Object.keys(activeStore)[i];
			if (Object.keys(activeStore).length > 0) generateRequest(str);
		}

		$('.applyContainer').on('change', 'select', e => { 
			var offset = $('#pageCount option:selected').text() - 1;
			var maxLength = ((offset * 25) + 25 <= Object.keys(activeStore).length) ? (offset * 25) + 25 : Object.keys(activeStore).length;
			var str = [];
			var count = 0;
			for (var i = offset * 25; i < maxLength; i++) str[count++] = Object.keys(activeStore)[i];
			generateRequest(str);
		});

		$('#ehx-clear').click(e => { // TODO: I want to condense these three blocks -----
			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, false);
					$('#ehx-clear').text('Clear Data');
					galleries = JSON.parse('{"data":{}}');
					$('#gLength').text(Object.keys(galleries).length);
					$('.ehx-visited').removeClass('ehx-visited');
					addCSS();
				}
			}
		});
		$('#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');
						hidden = JSON.parse('{"data":{}}');
						$('#hLength').text(Object.keys(hidden).length);
						$('.ehx-hidden').removeClass('ehx-hidden');
						addCSS();
					}
				}
			}
		});
		$('#ehxd-clear').click(e => {
			var objStore2 = db.transaction('down', 'readwrite').objectStore('down');
			var openReq = objStore2.getAll();
			openReq.onsuccess = e => {
				if (!ehxdClearConfirm) {
					ehxdClearConfirm = 1;
					$('#ehxd-clear').append(': Are you sure?');
				} else {
					var objStore3 = db.transaction('down', 'readwrite').objectStore('down');
					var openReq = objStore3.clear();
					openReq.onsuccess = e => {
						displayAlert('Cleared all entries', 5000);
						$('#ehxd-clear').text('Clear Data');
						$('.ehx-downloaded').removeClass('ehx-downloaded');
						addCSS();
					}
				}
			}
		}); // ------//

		// 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'>
#ehx-controls {
	padding: 3px 1px;
	text-align: center;
	display: block;
}
#ehx-settings, #ehx-show, #ehxh-show {
    cursor: pointer;
    text-decoration: underline;
}
#hideCount > span { border-bottom: 1px dotted currentColor; }
#importGalleries, #exportGalleries { min-height: 414px; }
#settings-close {
	text-decoration: none;
	position: absolute;
	top: 0px;
	right: 5px;
	font-size: 1.4em;
}
@-moz-document url-prefix() {
    #settings-close {
        top: -2px;
        -webkit-text-stroke: 1px;
    }
}
#ehx-export, #ehxh-export, #ehx-import, #ehxh-import, #settings-close { cursor: pointer; }
#topNav {
    border-bottom: 1px solid threedface;
    left: -4px;
    min-width: 898px;
}
#visControls { top: -6px; }
div > .imgHide {
	cursor: pointer !important;
	position: absolute;
	bottom: 3px;
	left: 2px;
}
ehx { font-family:` + $('body').css('font-family') + `, arial, symbola, SymbolaRegular;  }
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;
}
section:nth-child(4) fieldset {
	padding: 0px;
	min-height: 467px;
}
td.hideContainer .imgHide {
	cursor: pointer !important;
	vertical-align: middle;
}
.active:after { content: '\\2212' !important; }
.alertContainer {
    position: fixed;
    width: 100%;
    z-index: 200;
    top: 0px;
}
.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;
}
.category {
	text-align: center;
	position: absolute;
	left: 115px;
	bottom: 3px;
	line-height: 20px;
}
.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;
}
.date {
	font-style: italic;
	font-weight: bold;
}
.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-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-minimal {
    border-left: 1px solid #6f6f6f4d;
    text-align: center;
}
.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; }
.gltc ehx { white-space: nowrap; }
.gltc td.hideContainer {
	border-bottom: 1px solid #6f6f6f4d;
	border-top: 1px solid #6f6f6f4d;

}
.glte .imgHide {
    cursor: pointer !important;
    padding: 4px 2px 0px 1px;
    top: 3px;
    right: 5px;
    left: initial;
    bottom: initial;
}`
 + (setStore.titleShow ? `div.gl4t:hover {
    overflow: visible;
    z-index: 3;
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    height: auto;
    max-height: none;
}
div.gl1t {
    min-height: 455px;
    position: relative;
}
div.gl3t {
    position: absolute;
    left: 50%;
    margin-left: -125px;
    top: 38px;
}
div.gl4t {
    font-weight: bold;
    text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
div.gl5t {
    position: absolute;
    bottom: 25px;
    left: 50%;
    margin-left: -100px;
}` : ``) + `
.hideControls { top: -3px; }
.imgContainer > .imgHide {
	cursor: pointer !important;
	position: absolute;
	top: 1px;
	right: 4px;
	bottom: unset;
	left: unset;
}
.inactive { display: none; }
.listBody {
	width: 100%;
	height: 100%;
	vertical-align: top;
	position: relative;
}
.listing {
	width: 100%;
	height: 140px;
	border-bottom: 1px solid threedface;
	margin-top: 10px;
	padding-bottom: 5px;
}
.listing a {
    text-decoration: none;
    position: relative;
    z-index: 1;
}
.listing:last-child { border-bottom: none; }
.mencon {
	display: inline-block;
	position: relative;
}
.menu:after {
	content: '\\2335';
	position: absolute;
	right: 5px;
	bottom: 1px;
}
.noscroll { overflow: hidden; }
.notice {
	position: relative;
	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;
}
.notice.alert {
	background: rgba(165, 42, 42, 0.8);
}
.notice:not(:first-child) {
    top: 30px;
}
.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; }
.rating {
	text-align: right;
	line-height: 18px;
	position: absolute;
	right: 5px;
	bottom: 8px;
}
.removed {
	opacity: 0.5;
	-webkit-opacity: 0.5;
	pointer-events: none;
}
.sControls {
	top: -3px;
	margin-bottom: 5px;
}
@-moz-document url-prefix() {
    .sControls {
        top: initial;
        margin-top: 3px;
        margin-bottom: 5px;
    }
}
.section-container {
	text-align: left;
	overflow: auto;
	margin: 5px 0px 5px 0px;
	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;
	margin-bottom: 5px;
}
.show { display:block }
.suboptions { position: relative; }
.suboptions > div {
	position: relative;
	padding-left: 1.4em;
}
.suboptions2 {
	margin-left: 4px;
	padding-left: 10px;
	margin-right: -9px;
}
.thumb {
	display: inline-block;
	width: 100px;
	margin: 0px 10px;
	float: left;
}
.thumb img {
	max-width: 100%;
	max-height: -webkit-fill-available;
}
.title { font-size: 12pt; }
</style>`).appendTo('head');
    $(`<style id="setStyle" data-jqstyle="ehxVisited">
table.itg > tbody > tr.ehx-visited, .gl1t.ehx-visited { ` + cssA.visible + ` }
table.itg > tbody > tr.ehx-visited.ehx-hidden, .gl1t.ehx-visited.ehx-hidden { ` + cssA.hidden + ` }
.ehx-hidden { ` + cssD + cssA.hidden + ` }
.ehx-downloaded { ` + cssA.download + ` }
.ehx-hidden[data-jqstyle*="f"] {` + cssA.filter + `}
.ehx-hidden[data-jqstyle*="p"] {` + cssA.page + `}
.ehx-hidden[data-jqstyle*="r"] {` + cssA.rating + `}
.ehx-hidden[data-jqstyle*="u"] {` + cssA.uploader + `}
</style>`).appendTo('head');
})();