您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
E-H Visited, combined with ExVisited, and then better.
当前为
// ==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">✖</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">✖</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">🞫</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 class="suboptions"> <div> <span class="branch">∟</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') ? ' 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); } /** * 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>	<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 ▾</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'); })();