// ==UserScript==
// @name EhxVisited
// @namespace https://sleazyfork.org/en/users/285675-hauffen
// @version 2.53.52.2
// @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;", "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
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;
var filterArr = [], uploaderArr = [];
var observer = new MutationObserver(e => {
addCSS();
});
var spl = document.URL.split('/');
var d1 = spl[3];
var galleries, hidden;
var ehxClearConfirm = 0, ehxhClearConfirm = 0, reload = 0;
if (d1 == 'g') addGallery(); // Add the current page to galleries
if (d1.substr(0, 1) == '?' || d1.substr(0, 1) == 'w' || d1.substr(0, 1) == '#' || d1.substr(0, 1) == 'f' || d1.substr(0, 1) == 'p' || d1.substr(0, 1) == 't' || !d1) {
$('<div class="alertContainer"></div>').appendTo('body');
populate();
populateFilter();
}
/**
* 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 != '') { // There's probably some way that I could condense this
var tempArr = filters.title.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;
}
filterArr.push(regex);
}
}
if (filters.uploader != '') {
var tempArr2 = filters.uploader.split('\n');
for (i = 0; i < tempArr2.length; i++) {
if (tempArr2[i].startsWith('#')) continue;
try {
regex = new RegExp(tempArr2[i], 'i');
} catch (e) {
displayAlert('Invalid Regex On Line ' + (i + 1), 5000, true);
continue;
}
uploaderArr.push(regex);
}
}
}
/**
* Add a gallery to our IndexedDB
*/
function addGallery() {
const request = indexedDB.open('ehxvisited', 1);
request.onupgradeneeded = e => { // Generate our database if it's not there
db = e.target.result;
if (!db.objectStoreNames.contains('galleries')) db.createObjectStore('galleries', {keyPath: 'id'});
if (!db.objectStoreNames.contains('hidden')) db.createObjectStore('hidden', {keyPath: 'id'});
};
request.onsuccess = e => {
db = e.target.result;
var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries');
var openRequest = objStore.openCursor(spl[4] + '.' + spl[5]);
openRequest.onsuccess = e => {
var cursor = openRequest.result;
if (cursor) { // Update entry if key exists
cursor.update({id: spl[4] + '.' + spl[5], visited: Date.now()});
console.log('EhxVisited: Updated ' + spl[4] + '.' + spl[5]);
} else { // Otherwise, add entry
objStore.add({id: spl[4] + '.' + spl[5], visited: Date.now()});
console.log('EhxVisited: Added ' + spl[4] + '.' + spl[5]);
}
};
openRequest.onerror = e => {
console.log(`EhxVisited: Something bad happened with gallery ${spl[4]}.${spl[5]}: ${e.target.error}`);
};
};
};
/**
* Updates the hidden gallery count in the header object
*/
function updateGListing() {
// There's probably a better way to do this portion rather than iterate through all current objects, but runtime is negligible
var list = $('.itg .gl1t').length > 0 ? $('.itg .gl1t') : $('table.itg>tbody>tr').has('.glhide, .gldown, th'); // Get the proper elements depending on our view mode
var hAmount, vAmount, fAmount, pAmount, rAmount, uAmount, hCount;
hAmount = vAmount = fAmount = pAmount = rAmount = uAmount = hCount = 0;
for (var i = 0; i < list.length; i++) {
if ($(list[i]).hasClass('ehx-hidden')) { // Count generic Hidden galleries
if ($(list[i]).hasClass('ehx-visited')) vAmount++;
hCount++;
}
if ($(list[i]).attr('data-jqstyle')) { // Count elements via JQ Style tags
if ($(list[i]).attr('data-jqstyle').match('h')) hAmount++;
if ($(list[i]).attr('data-jqstyle').match('f')) fAmount++;
if ($(list[i]).attr('data-jqstyle').match('p')) pAmount++;
if ($(list[i]).attr('data-jqstyle').match('r')) rAmount++;
if ($(list[i]).attr('data-jqstyle').match('u')) uAmount++;
}
if ($('#ehx-show').text() === 'Show') { // If Visited Galleries are hidden, count those as well
if ($(list[i]).hasClass('ehx-visited')) {
vAmount++;
hCount++;
}
}
}
if (!setStore.softHide) {
$('#hideCount').html('There ' + (hCount > 1 || hCount == 0 ? 'are ' : 'is ') + '<span>' + hCount + ' hidden ' + (hCount > 1 || hCount == 0 ? 'galleries' : 'gallery') + '</span> on this page.');
} else {
$('#hideCount').html('There are <span>0 hidden galleries</span> on this page.');
}
$('#hideCount > span').prop('title', 'Hidden: ' + hAmount + ' | Visited: ' + vAmount + ' | Filtered: ' + fAmount + ' | Page Limit: ' + pAmount + ' | Rating Limit: ' + rAmount + ' | Uploader: ' + uAmount);
}
/**
* Convert the star count of a specified element to a double
* @param {Object} el - A specific element within the DOM
*/
function getStarNumber(el) {
var stars = $(el).find('.ir').css('background-position');
switch(stars) {
case "0px -1px":
return 5;
case "0px -21px":
return 4.5;
case "-16px -1px":
return 4;
case "-16px -21px":
return 3.5;
case "-32px -1px":
return 3;
case "-32px -21px":
return 2.5;
case "-48px -1px":
return 2;
case "-48px -21px":
return 1.5;
case "-64px -1px":
return 1;
case "-64px -21px":
return 0.5;
default:
return 0;
}
}
/**
* Check a specified element through the filters individually, then apply jqstyle tags for CSS
* @param {Object} el - A specific element within the DOM
*/
function filterCheck(el) {
if (filterArr.length) {
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) < 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', 1);
var tgid = tga.split('/')[4] + '.' + tga.split('/')[5];
request.onsuccess = e => {
db = e.target.result;
var objStore = db.transaction('hidden', 'readwrite').objectStore('hidden');
var openReq = objStore.openCursor(tgid);
openReq.onsuccess = e => {
var cursor = e.target.result;
if (cursor) { // Gallery already exists within our hidden table
cursor.delete();
console.log('EhxVisited: Removed ' + tgid + ' from hidden list.');
$(el).css('display', '');
$(el).removeClass('ehx-hidden');
removeStyle($(el), 'h');
delete hidden.data[tgid]; // Remove gallery listing from our local store of hidden galleries
} else {
objStore.put({id: tgid}); // Put the gallery into our hidden table
console.log('EhxVisited: Added ' + tgid + ' to hidden list.');
$(el).addClass('ehx-hidden');
if ($('#ehxh-show').text() === 'Hide') $(el).css('display', $('.gl1t').length ? 'flex' : 'table-row');
addStyle($(el), 'h');
hidden.data[tgid] = 1; // Add gallery listing to our local store of hidden galleries
}
updateGListing();
$('#hLength').text(Object.keys(hidden.data).length);
}
}
if ($('.glte > tbody > tr').first().hasClass('ehx-hidden') && !setStore.hidShow) $('.glte > tbody > tr:not(.ehx-hidden):first .imgHide').css('border-top', 'none'); // Because aesthetics
else $('.imgHide').removeAttr('style');
}
/**
* Fill our local gallery listings so we can preform easier operations on the data.
* Also set up the majority of our global HTML elements and their functions.
*/
function populate() { // TODO: Separate the HTML entries from the population portion
galleries = JSON.parse('{"data":{}}');
hidden = JSON.parse('{"data":{}}');
const request = indexedDB.open('ehxvisited', 1);
request.onupgradeneeded = e => {
db = e.target.result;
if (!db.objectStoreNames.contains('galleries')) db.createObjectStore('galleries', {keyPath: 'id'});
if (!db.objectStoreNames.contains('hidden')) db.createObjectStore('hidden', {keyPath: 'id'});
};
request.onsuccess = e => {
db = e.target.result;
var objStore = db.transaction('galleries', 'readonly').objectStore('galleries');
var openReq = objStore.getAll();
openReq.onsuccess = f => {
console.log('EhxVisited: Populated global variables.');
var transform = f.target.result;
for (var i = 0; i < transform.length; i++) {
galleries.data[transform[i].id] = transform[i].visited; // Force matrix data into array data
}
var gLength = Object.keys(galleries.data).length
var objStore2 = db.transaction('hidden', 'readonly').objectStore('hidden');
var openReq2 = objStore2.getAll();
openReq2.onsuccess = g => {
var transform2 = g.target.result;
for (i = 0; i < transform2.length; i++) {
hidden.data[transform2[i].id] = 1; // Force matrix data into array data
}
var hLength = Object.keys(hidden.data).length
$($('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'});
}
$('.imgHide').removeAttr('style'); // aesthetics
} else {
$('.ehx-hidden').css({display: ''});
$('.ehx-visited.ehx-hidden').css({display: ''});
if ($('.glte > tbody > tr').first().hasClass('ehx-hidden')) $('.glte > tbody > tr:not(.ehx-hidden):first .imgHide').css('border-top', 'none'); // a e s t h e t i c s
}
$('#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();
if ($('.glte > tbody > tr').first().hasClass('ehx-hidden') && !setStore.hidShow) $('.glte > tbody > tr:not(.ehx-hidden):first .imgHide').css('border-top', 'none'); // A E S T H E T I C S
}
}
}
}
/**
* 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() {
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.data[galleryId] != undefined) { // Visited
if (!$(list[i]).hasClass('ehx-visited')) {
$(list[i]).addClass('ehx-visited');
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));
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 { // 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');
}
if (hidden.data[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
$(list[i]).addClass('ehx-hidden');
addStyle($(list[i]), 'h');
}
if ($(list[i]).children().length < 3 || ($(list[i]).children().length < 4 && onFavs)) { // 2 in normal, 3 in favorites
$('<img class="imgHide" src="' + img_hide + '" title="Show/Hide Gallery">').prependTo($(list[i]).find('a').first().parents().eq(2)).click(e => { // Maybe closest('tr')
toggleElement($(e.currentTarget).parent().find('a').attr('href'), $(e.currentTarget).parent());
});
}
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">✖</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.data[galleryId] != undefined) { // Visited
if (!$(list[i]).hasClass('ehx-visited')) {
$(list[i]).addClass('ehx-visited');
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));
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(0, 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(0, 10) + '</ehx></td>');
}
}
} 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>');
}
}
if (hidden.data[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
$(list[i]).addClass('ehx-hidden');
addStyle($(list[i]), 'h');
}
var childCount = $(list[i]).children().length;
if (childCount < 5 || (childCount < 6 && setStore.minAdd) || (childCount < 7 && onFavs)) { // Fuck this conditional
$('<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">✖</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.data[galleryId] != undefined) { // Visited
if (!$(list[i]).hasClass('ehx-visited')) {
$(list[i]).addClass('ehx-visited');
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));
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 { // 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>');
}
}
if (hidden.data[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
$(list[i]).addClass('ehx-hidden');
addStyle($(list[i]), 'h');
}
childCount = $(list[i]).children().length;
if (childCount < 7 || (childCount < 8 && setStore.minAdd) || (childCount < 8 && onFavs) || (childCount < 9 && setStore.minAdd && onFavs)) { // Fuck this conditional harder
$('<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.data[galleryId] != undefined) { // Visited
if (!$(list[i]).hasClass('ehx-visited')) {
$(list[i]).addClass('ehx-visited');
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));
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 { // 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');
}
if (hidden.data[galleryId] != undefined && !$(list[i]).hasClass('ehx-hidden')) {
$(list[i]).addClass('ehx-hidden');
addStyle($(list[i]), 'h');
}
if ($(list[i]).find('.gl5t').children().length < 3 || ($(list[i]).find('.gl5t').children().length < 4 && setStore.titleShow)) {
$('<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.data[gid]);
var str = d.getFullYear().toString() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' ' + 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.data[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') ? ' sec' : ' seconds ago');
} else if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + ((typeof abbreviate !== 'undefined') ? ' min' : ' minutes ago');
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + ((typeof abbreviate !== 'undefined') ? ' hrs' : ' hours ago');
} else if (elapsed < msPerMonth) {
return Math.round(elapsed / msPerDay) + ((typeof abbreviate !== 'undefined') ? ' days' : ' days ago');
} else if (elapsed < msPerYear) {
return Math.round(elapsed / msPerMonth) + ((typeof abbreviate !== 'undefined') ? ' mos' : ' months ago');
} else {
return Math.round(elapsed / msPerYear) + ((typeof abbreviate !== 'undefined') ? ' 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);
}
/**
* Fills a text area with formatted gallery data for export
* @param {IndexedDB Matrix} items - Raw IndexedDB getAll output
*/
function ehxExport(items) {
var data = '';
for (var i in items) {
data += items[i].id + ':' + items[i].visited + ';';
}
$('#exportGalleries').val(''); // Clear text area
$('#exportGalleries').val(data); // Fill with formatted data
$('#exportGalleries').select();
}
/**
* 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.data[e.currentTarget.href.split('/')[4] + '.' + e.currentTarget.href.split('/')[5]] = Date.now();
$('#gLength').text(Object.keys(galleries.data).length);
addCSS(); // Probably edit to update timstamps too since it's already iterating through all the gallery data
}
});
/**
* Import user data into our indexedDB
* @param {String} store - An object store within the indexedDB
* @param {JSON Array} items - String of exported data to import
*/
function ehxImport(store, items) {
var objStore2 = db.transaction(store, 'readwrite').objectStore(store);
var count = 0, sp = '';
sp = items.split(';');
sp = sp.filter(Boolean); // Filter out any null ('') entries
insertNext();
/**
* Push entries into the specified indexedDB store
*/
function insertNext() {
if (count < sp.length) {
var str = sp[count].split(':');
objStore2.put({id: str[0], visited: parseInt(str[1])}).onsuccess = insertNext; // Update the record if it's there, or add it if it's not, then continue
if (store == 'galleries') galleries.data[str[0]] = str[1];
else hidden.data[str[0]] = str[1];
++count;
} else {
displayAlert('Imported ' + count + ' entries', 5000);
console.log('EhxVisited: Merge Completed');
$('#importGalleries').val('');
$('#gLength').text(Object.keys(galleries.data).length);
$('#hLength').text(Object.keys(hidden.data).length);
addCSS();
}
}
}
/**
* Remove our stylesheet with transient CSS, and then readd 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-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() {
const req = indexedDB.open('ehxvisited', 1);
req.onsuccess = e => {
if (db == null) db = e.target.result;
var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries');
var openReq = objStore.getAll();
openReq.onsuccess = e => {
var len = e.target.result;
// There's probably a much easier way to do this, or at least a nicer looking, more technical way
var container = $(`
<div class="overlay">
<div class="settings">
<nav id="topNav">
<span id="setNotice" style="float: left; margin-left: 8px; margin-top: 2px; font-weight: lighter; opacity: 0.5; -webkit-opacity: 0.5;">` + (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>
</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>
</div>
</div>
<a id="settings-close">🞫</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">∟</span>
<label>
<input type="checkbox" id="minShow" ` + (setStore.minShow ? `checked` : ``) + `>Minimal Show Text
</label>
<span>: Show visits as text instead of hovering tooltip in Minimal/Minimal+ view modes</span>
</div>
</div>
</div>
<div>
<label>
<input type="checkbox" id="cssTT" ` + (setStore.cssTT ? `checked` : ``) + `>CSS Tooltips
</label>
<span>: Replace gallery link tooltips with visited information in all view modes</span>
</div>
<div>
<label>
<input type="checkbox" id="repPub" ` + (setStore.repPub ? `checked` : ``) + `>Replace Published
</label>
<span>: Replace date published with date visited in Minimal/Minimal+ view modes</span>
</div>
<div>
<label>
<input type="checkbox" id="titleShow" ` + (setStore.titleShow ? `checked`: ``) + `>Show Full Title
</label>
<span>: Show the full title of a gallery on hover in Thumbnail view</span>
</div>
</fieldset>
<fieldset>
<legend>Custom CSS</legend>
<h3>Visited Galleries
<div class="control" id="visControls">
<button id="resV">Reset CSS</button>
<button 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 sControls" id="hideControls">
<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>
<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; min-height: 474px;">
<legend id="importTitle">Import Galleries</legend>
<textarea id="importGalleries"></textarea>
<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
<button class="close">Close</button>
<button id="importConfirm">Import</button>
</div>
</fieldset>
</section>
<section class="inactive">
<fieldset style="padding-bottom: 2px; min-height: 474px;">
<legend id="exportTitle">Export Galleries</legend>
<textarea id="exportGalleries"></textarea>
<div class="control" style="margin-top: 2px; margin-bottom: 4px;">
<button class="close">Close</button>
<button id="exportCopy">Copy</button>
</div>
</fieldset>
</section>
</div>
<div class="applyContainer">
<div class="control" style="padding-right: 5px;">
<button id="apply">Apply</button>
</div>
</div>
</div>
</div>`);
$('body').append(container);
if (!$('#minAdd').prop('checked')) { $('#minShow').prop('disabled', true); }
$('#resV').click(e => { $('#visited').val('box-shadow: inset 0 0 0 500px rgba(2, 129, 255, .2) !important;'); }); // Default Values
$('#resH').click(e => { $('#hidden').val('box-shadow: inset 0 0 0 500px rgba(255, 40, 0, .2) !important;'); });
$('#resF').click(e => { $('#filtered').val('box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;'); });
$('#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').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');
}
});
$('#apply').click(e => applySettings());
/**
* Parse our HTML options into a temporary JSON array and then stringify it into localStorage
*/
function applySettings() {
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(),
"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';
});
$('#ehx-import').click(e => {
$('.section-container section').addClass('inactive');
$('.section-container section:nth-child(2)').removeClass('inactive');
$('#importTitle').text('Import Galleries');
$('#importGalleries').val('');
});
$('#ehxh-import').click(e => {
$('.section-container section').addClass('inactive');
$('.section-container section:nth-child(2)').removeClass('inactive');
$('#importTitle').text('Import Hidden Galleries');
$('#importGalleries').val('');
});
$('#importConfirm').click(e => ehxImport($('#importTitle').text().search(/hidden/i) != -1 ? 'hidden' : 'galleries', $('#importGalleries').val().replace(/^\s*[\r\n]/gm, '')));
$('.close').click(e => {
$('.section-container section').addClass('inactive');
$('.section-container section:first-child').removeClass('inactive');
});
$('#exportCopy').click(e => {
$('#exportGalleries').select();
document.execCommand('copy');
displayAlert('Copied Text To Clipboard', 5000);
});
$('#ehx-export').click(e => {
$('.section-container section').addClass('inactive');
$('.section-container section:nth-child(3)').removeClass('inactive');
$('#exportTitle').text('Exported Galleries');
ehxExport(len);
});
$('#ehxh-export').click(e => {
var objStore2 = db.transaction('hidden', 'readwrite').objectStore('hidden');
var openReq = objStore2.getAll();
openReq.onsuccess = e => {
$('.section-container section').addClass('inactive');
$('.section-container section:nth-child(3)').removeClass('inactive');
$('#exportTitle').text('Exported Hidden Galleries');
ehxExport(e.target.result);
}
});
$('#ehx-clear').click(e => {
if (!ehxClearConfirm) { // Make sure to double check before deleting
ehxClearConfirm = 1;
$('#ehx-clear').append(': Are you sure?');
} else {
var objStore2 = db.transaction('galleries', 'readwrite').objectStore('galleries');
var openReq = objStore2.clear();
openReq.onsuccess = e => {
displayAlert('Cleared all entries', 5000, false);
$('#ehx-clear').text('Clear Data');
galleries = JSON.parse('{"data":{}}');
$('#gLength').text(Object.keys(galleries.data).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.data).length);
$('.ehx-hidden').removeClass('ehx-hidden');
addCSS();
}
}
}
});
// Fancy function to make sure there's not more than one top menu item open
$('.menu').click(e => {
if ($('.show').length) {
if ($('.show').prev().is(e.target)) { $(e.target).next().toggleClass('show'); }
else {
$('.show').removeClass('show');
$(e.target).next().toggleClass('show');
}
} else $(e.target).next().toggleClass('show');
});
}
}
}
// The giant CSS block
$(`<link rel="stylesheet" media="screen" href="https://fontlibrary.org/face/symbola" type="text/css"/>
<style data-jqstyle='ehxVisited'>
#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: 420px; }
#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;
}
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;
}
.collapsible {
cursor: pointer;
width: 100%;
border: 0;
outline: none;
text-align: left;
font-size: 1.25em;
background-color: rgba(0, 0, 0, 0);
color: inherit;
font-weight: bold;
padding:5px 3px;
position: relative;
}
.collapsible:after {
content: '\\002B';
font-weight:bold;
float:right;
margin-right:5px;
}
.collapsible:before {
content: '';
position: absolute;
padding: 4px;
border-bottom: 1px solid threedface;
border-left: 1px solid threedface;
top: 5px;
left: -11px;
}
.collapsible:hover { background-color: rgba(255, 255, 255, 0.1); }
.content {
max-height: 0;
overflow: hidden;
transition: all .2s ease-in-out;
border-bottom: 1px solid threedface;
}
.content button {
margin-top: 3px;
margin-right: 10px;
}
.control {
position: relative;
float: right;
right: -5px;
}
.dropdown {
display: none;
position: absolute;
z-index: 999;
min-width: 150px;
padding: 2px;
border-radius: 1px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
right: 0px;
border: 1px solid threedface;
background: ` + $('.ido').css('background') + `;
background: ` + $('.ido').css('backgroundColor') + `;
}
.dropdown a {
display: block;
text-decoration: none;
padding-right:2px;
}
.dropdown a:hover { background: rgba(255,255,255,0.2); }
.ehx-compact {
border-style: solid;
border-width: 1px 0;
text-align: center;
}
.ehx-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 td.hideContainer {
border-bottom: 1px solid #6f6f6f4d;
border-top: 1px solid #6f6f6f4d;
}
.glte .imgHide {
cursor: pointer !important;
padding: 4px 2px 0px 1px;
` + ($('.glfe').length ? `border-top: 1px solid #6f6f6f4d;` : `border-top: 1px solid #6f6f6f;`) + `
}
.glte tr:nth-child(1) .imgHide {
border-top: none;
}`
+ (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;
}` : ``) + `
.inactive { display: none; }
.mencon {
display: inline-block;
position: relative;
}
.menu:after {
content: '\\2335';
position: absolute;
right: 5px;
bottom: 1px;
}
.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; }
.sControls { top: -3px; }
@-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;
}
.show { display:block }
.suboptions { position: relative; }
.suboptions > div {
position: relative;
padding-left: 1.4em;
}
.suboptions2 {
margin-left: 4px;
padding-left: 10px;
margin-right: -9px;
}
</style>`).appendTo('head');
$(`<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-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');
})();