Sleazy Fork is available in English.

EhxVisited

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

Verzia zo dňa 18.03.2021. Pozri najnovšiu verziu.

// ==UserScript==
// @name         EhxVisited
// @namespace    https://sleazyfork.org/en/users/285675-hauffen
// @version      2.57.13.7
// @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, "titleOn": false};
    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;';
	const 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 galleries, hidden, down;
    var activeStore, activeStoreTitle;
    var d1 = spl[3], reload = 0, tags = [];
	const category = {doujinshi: 'ct2', manga: 'ct3', artistcg: 'ct4', gamecg: 'ct5', western: 'cta', nonh: 'ct9', imageset: 'ct6', cosplay: 'ct7', asianporn: 'ct8', misc: 'ct1'};
    const fileSizeLabels = [ "B", "KB", "MB", "GB" ];

    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).match(/[?#fnptw]/i) && !d1.startsWith('toplist')) || Object.keys(category).includes(d1) || !d1) {
        $('<div class="alertContainer"></div>').appendTo('body');
        populate();
    }

    /**
     * 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}`);
            };
        };
    };

	/**
	 * 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();
					}
                }
            }
        }
    }

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

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

    /**
	 * 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_t"><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
		});
	}

	/**
     * Apply visited CSS to an element on click
     */
    $('.itg').on('click', '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();
        }
    });

    $('.itg').on('auxclick', '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();
        }
    });

	/**
	 * 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 class="suboptions">
                                    <div>
                                        <span class="branch">&#8735</span>
                                        <label>
                                            <input type="checkbox" id="titleOn" ` + (setStore.titleOn ? `checked` : ``) + `>Always Show Titles
                                        </label>
                                        <span>: Show full title of a gallery always in Thumbnail view</span>
                                    </div>
                                </div>
							</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; width: 100%; text-align: right;">
                                <label style="float: left;">
                                    <input type="checkbox"  id="rawUrl">Import Raw URLs
                                </label>
								<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); }
        if (!$('#titleShow').prop('checked')) { $('#titleOn').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 ($('#titleShow').prop('checked')) $('#titleOn').prop('disabled', false);
            else $('#titleOn').prop('disabled', true);

			if ($('#minAdd').is(e.target) || $('#minShow').is(e.target) || $('#repPub').is(e.target) || $('#titleShow').is(e.target) || $('#titleOn').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,
                "titleOn": setStore.titleOn
			}
			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'),
                "titleOn": $('#titleOn').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, ''), $('#rawUrl').prop('checked')));

		$('.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 (confirm('Are you sure you wish to clear all visited galleries?')) { // Make sure to double check before deleting
				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 (confirm('Are you sure you wish to clear all hidden galleries?')) {
					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 (confirm('Are you sure you wish to clear all downloaded galleries?')) {
					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');
		});
    }

    /**
	 * 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
        filterArrCheck(filterArr, $(el).find('.glink').text(), 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();
        filterArrCheck(uploaderArr, upload, 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');
    }

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

    /**
     * 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;
	}

	/**
     * 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
    }

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

    /**
     * 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);
        generateTags(glisting);
        let taglist = "<table><tbody>";
        for (const namespace in tags) {
            taglist += `<tr><td class="tc">${namespace}:</td><td>`;
            for (var i = 0; i < tags[namespace].length; i++) {
                taglist += `<div id="td_${namespace}:${tags[namespace][i]}" class="gt" style="opacity:1.0"><a id="ta_${namespace}:${tags[namespace][i]}" href="${document.location.origin}/tag/${namespace}:${tags[namespace][i]}">${tags[namespace][i]}</a></div>`;
            }
            taglist += "</td></tr>";
        }
        taglist += "</tbody></table>";
        let torrentList = "";
        if (glisting.torrentcount > 0) {
            torrentList = `<div class='torrents'style='border-top:1px solid #000; padding: 10px 10px 5px 10px; margin-right: 10px;'><p><span class='halp' title="If these links don't work, this gallery likely had a parent gallery">Possible Torrents:</span></p>`;
            for (var j = 0; j < glisting.torrentcount; j++) {
                torrentList += `<div><img src="` + (window.location.hostname.indexOf("exhentai") >= 0 ? "https://exhentai.org/img/mr.gif" : "https://ehgt.org/g/mr.gif") + `" /><a class="tlink" title="` + glisting.torrents[j].name + `" href="` + generateTorrentLink(glisting, j) + `">` + glisting.torrents[j].name + `</a>&#9;<span>` + getPrettyFileSize(glisting.torrents[j].fsize) + `</span><span style="float: right; position: relative; top: 3px;">Added ` + getTimestampDateString(glisting.torrents[j].added * 1000) + `</span></p></div>`;
            }
            torrentList += "</div>";
        }
		// 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>
                    <p>` + glisting.title_jpn + `</p>
				</div>
                <div class="taglist">` + taglist + torrentList + `</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">
						` + getTimestampDateString(d).slice(0, 16) + `
					</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 class="tagToggle"><a>Show Tags and Torrents &#x25BE;</a></div>
			</div>
		</div>`);
		$('#listingContainer').append(listing);
        $('.tagToggle > a').on('click', e => {
            if ($(e.currentTarget).parents().eq(1).find('.taglist').css('display') == 'none') {
                e.stopImmediatePropagation(); // Have to put this in because apparently, this event wants to fire like 12 times in a row
                $(e.currentTarget).parents().eq(1).find('.taglist').css('display', 'block');
                $(e.currentTarget).parents().eq(2).css('height', 'unset');
                $(e.currentTarget).parents().eq(2).css('height', parseInt($(e.currentTarget).parents().eq(2).css('height').substr(0, 3)) + 70 + 'px');
                $(e.currentTarget).text('Hide Tags and Torrents \u25B4');
            } else {
                e.stopImmediatePropagation(); // I have no idea why it does that, it just does
                $(e.currentTarget).parents().eq(1).find('.taglist').css('display', 'none');
                $(e.currentTarget).parents().eq(2).css('height', '140px');
                $(e.currentTarget).text('Show Tags and Torrents \u25BE');
            }
        });
		$('<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));
		});
	}

    function generateTags(apirsp) {
        tags = {}; // Reset the tags array for each new tag listing
		if (Array.isArray(apirsp.tags)) {
			for (const jsonTag of apirsp.tags) {
				const stringTag = getJsonString(jsonTag);
				if (stringTag === null) { continue; }

				const {tag, namespace} = getTagAndNamespace(stringTag);

				let namespaceTags;
				if (tags.hasOwnProperty(namespace)) { namespaceTags = tags[namespace]; }
                else {
					namespaceTags = [];
					tags[namespace] = namespaceTags;
				}
				namespaceTags.push(tag);
			}
		}
	}

    function generateTorrentLink(glisting, index) {
        if (window.location.hostname.indexOf("exhentai") > 0) return window.location.origin + "/torrent/" + glisting.gid + "/" + glisting.torrents[index].hash + ".torrent";
        else return "https://ehtracker.org/get/" + glisting.gid + "/" + glisting.torrents[index].hash + ".torrent";
    }

    function getTagAndNamespace(tag) {
		const pattern = /^(?:([^:]*):)?([\w\W]*)$/;
		const match = pattern.exec(tag);
		return (match !== null) ?
			({ tag: match[2], namespace: match[1] || "misc" }) :
			({ tag: tag, namespace: "misc" });
	}

	function toProperCase(text) {
		return text.replace(/(^|\W)(\w)/g, (m0, m1, m2) => `${m1}${m2.toUpperCase()}`);
	}

	function getJsonString(value) {
		if (typeof(value) === "string") { return value; }
		if (typeof(value) === "undefined" || value === null) { return value; }
		return `${value}`;
	}

    function getPrettyFileSize(bytes) {
	    const ii = fileSizeLabels.length - 1;
	    let i = 0;
	    while (i < ii && bytes >= 1024) {
		    bytes /= 1024;
		    ++i;
	    }
	    return `${bytes.toFixed(i === 0 ? 0 : 2)} ${fileSizeLabels[i]}`;
    }

    function getTimestampDateString(timestamp) {
	    const date = new Date(timestamp);
	    const year = date.getUTCFullYear().toString();
	    const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
	    const day = date.getUTCDate().toString().padStart(2, "0");
	    const hour = date.getUTCHours().toString().padStart(2, "0");
	    const minute = date.getUTCMinutes().toString().padStart(2, "0");
        const seconds = date.getUTCSeconds().toString().padStart(2, "0");
	    return `${year}-${month}-${day} ${hour}:${minute}:${seconds}`;
    }

    /**
	 * Import user data into our indexedDB
	 * @param {String} items - String of exported data to import
     * @param {Boolean} raw - Boolean representing if we're importing raw URLs
	 */
    function ehxImport(items, raw) {
		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 = '';

            if (raw) {
                var textArr = items.split('\n');
                var tempStr = '';
                for (var i = 0; i < textArr.length; i++) {
                    var str = textArr[i].split('/')
                    tempStr += str[4] + "." + str[5] + ":" + Date.now() + ";";
                }
                sp = tempStr.split(';'); // I just pulled this block from a function I wrote, it could be adapted better
            } else { 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');
    }

    // 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;
	font-size: 8pt;
}
.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;

}
.gl5t {
    height: 63px;
    position: relative;
}
.glte .imgHide {
    cursor: pointer !important;
    padding: 4px 2px 0px 1px;
    top: 3px;
    right: 5px;
    left: initial;
    bottom: initial;
}`
 + ((setStore.titleShow && !setStore.titleOn) ? `.itg > .gl1t > a:first-of-type {
	overflow: hidden;
	min-height: 32px;
	max-height: 32px;
	margin: 6px 4px 0;
	position: relative;
	display: block;
}
.itg > .gl1t > a:first-of-type > .glname {
	overflow: visible;
	min-height: auto;
	max-height: none;
	margin: 0;
}
.itg > .gl1t:hover > a:first-of-type,
.itg > .gl1t:hover > .glname {
    overflow: visible;
	z-index: 10;
}
.itg > .gl1t:hover > a:first-of-type > .glname,
.itg > .gl1t:hover > .glft > div:nth-child(1) {
    padding-bottom: 0.25em;
    height: fit-content;
    height: -moz-fit-content;
    background: rgba(0,0,0,0.5);
}
div.gl4t {
    font-weight: bold;
    text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}` : ``) + (setStore.titleOn ? `.itg > .gl1t > a:first-of-type {
	margin: 6px 4px 0;
	position: relative;
	display: block;
}
.itg > .gl1t > a:first-of-type > .glname {
	overflow: visible;
	min-height: auto;
	max-height: none;
	margin: 0;
}
.itg > .gl1t:hover > a:first-of-type,
.itg > .gl1t:hover > .glname {
    overflow: visible;
	z-index: 10;
}
.itg > .gl1t:hover > a:first-of-type > .glname,
.itg > .gl1t:hover > .glft > div:nth-child(1) {
    height: fit-content;
    height: -moz-fit-content;
    background: rgba(0,0,0,0.5);
}
.itg > .gl1t > .glft > div:nth-child(1) {
    padding-bottom: 0.25em;
}
div.gl4t {
    height: fit-content !important;
    height: -moz-fit-content !important;
    max-height: none;
    padding-bottom: 0.25em;
    font-weight: bold;
    text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}` : ``) + `
.hideControls { top: -3px; }
.hideContainer_t {
    top: -17px;
}
.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;
	text-align: center;
}
.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;
}
.taglist {
    display: none;
    padding-left: 120px;
}
.tagToggle {
    position: absolute;
    left: 50%;
    bottom: 8px;
}
.thumb {
	display: inline-block;
	width: 100px;
	margin: 0px 10px;
	float: left;
}
.thumb img {
	max-width: 100%;
	max-height: 135px;
}
.title { font-size: 12pt; }
.title p {
    font-size: 0.6em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tlink {
    display: inline-block;
    top: 3px;
    margin-right: 1.5px;
    padding-left: 3px;
    width: 400px;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}
</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');
})();