您需要先安装一个扩展,例如 篡改猴、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.50.43 // @description E-H Visited, combined with ExVisited, and then better. // @author Hauffen // @require https://code.jquery.com/jquery-3.3.1.min.js // @include /https?:\/\/(e-|ex)hentai\.org\/.*/ // ==/UserScript== (function() { window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: 'readwrite'}; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; if (!window.indexedDB) { console.warn("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available."); return; } /*═════════════════════════════╗ ║ Configuration Defaults ║ ╚═════════════════════════════*/ var setStore = JSON.parse(localStorage.getItem('ehx-settings')) ? JSON.parse(localStorage.getItem('ehx-settings')) : JSON.parse('{"softHide":false, "minAdd":true, "minShow":false, "cssTT":false, "repPub":false, "visHide":false, "hidShow":false, "pFilter":false, "pLimit":0, "stFilter":false, "stLimit":0, "titleShow":true}'); var filters = localStorage.getItem('ehx-filters') ? localStorage.getItem('ehx-filters') : '#\\[Erocolor\\]'; var cssA = JSON.parse(localStorage.getItem('ehx-css')) ? JSON.parse(localStorage.getItem('ehx-css')) : JSON.parse('{"visible":"box-shadow: inset 0 0 0 500px rgba(2, 129, 255, .2) !important;", "hidden":"box-shadow: inset 0 0 0 500px rgba(255, 40, 0, .2) !important;", "filter":"box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;", "page":"box-shadow: inset 0 0 0 500px rgba(0, 0, 180, .2) !important;", "rating":"box-shadow: inset 0 0 0 500px rgba(180, 80, 60, .2) !important;"}'); var cssD = (setStore.softHide) ? 'opacity:0.2; -webkit-opacity: 0.2;' : 'display: none;'; /*════════════════════════════*/ let db = null; var filterArr = []; var pIS = 0; // Post Infinite Scroll var observer = new MutationObserver(e => { pIS = true; // Triggered on mutation, should never be false at this point though addCSS(); }); var spl = document.URL.split('/'); var d1 = spl[3]; var galleries, hidden; var ehxClearConfirm = 0, ehxhClearConfirm = 0; if (d1 == 'g') addGallery(); // Add the current page to galleries if (d1.substr(0, 1) == '?' || d1.substr(0, 1) == 'w' || d1.substr(0, 1) == '#' || d1.substr(0, 1) == 'f' || d1.substr(0, 1) == 't' || !d1) { populateFilter(); populate(); } /** * Populate the custom filter array with user input converted into regular expressions */ function populateFilter() { if (filters != '') { var tempArr = filters.split('\n'); for(var i = 0; i < tempArr.length; i++) { if (tempArr[i].startsWith('#')) continue; filterArr.push(new RegExp(tempArr[i], 'i')); } } } /** * Add a gallery to our IndexedDB */ function addGallery() { const request = indexedDB.open('ehxvisited', 1); request.onupgradeneeded = e => { // Generate our database if it's not there db = e.target.result; if (!db.objectStoreNames.contains('galleries')) { db.createObjectStore('galleries', {keyPath: 'id'}); } if (!db.objectStoreNames.contains('hidden')) { db.createObjectStore('hidden', {keyPath: 'id'}); } }; request.onsuccess = e => { db = e.target.result; var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries'); var openRequest = objStore.openCursor(spl[4] + '.' + spl[5]); openRequest.onsuccess = e => { var cursor = openRequest.result; if (cursor) { // Update entry if key exists cursor.update({id: spl[4] + '.' + spl[5], visited: Date.now()}); console.log('EhxVisited: Updated ' + spl[4] + '.' + spl[5]); } else { // Otherwise, add entry objStore.add({id: spl[4] + '.' + spl[5], visited: Date.now()}); console.log('EhxVisited: Added ' + spl[4] + '.' + spl[5]); } }; openRequest.onerror = e => { console.log(`EhxVisited: Something bad happened with gallery ${spl[4]}.${spl[5]}: ${e.target.error}`); }; }; }; /** * Updates the hidden gallery count in the header object */ function updateGListing() { // There's probably a better way to do this portion rather than iterate through all current objects, but runtime is negligible var list = $('.itg .gl1t').length > 0 ? $('.itg .gl1t') : $('table.itg>tbody>tr').has('.glhide, .gldown, th'); // Get the proper elements depending on our view mode var hAmount, vAmount, fAmount, pAmount, rAmount, hCount; hAmount = vAmount = fAmount = pAmount = rAmount = hCount = 0; for (var i = 0; i < list.length; i++) { if ($(list[i]).hasClass('ehx-hidden')) { // Count generic Hidden galleries if ($(list[i]).hasClass('ehx-visited')) vAmount++; hCount++; } if ($(list[i]).attr('data-jqstyle') != undefined) { // Count elements via JQ Style tags if ($(list[i]).attr('data-jqstyle').match('h')) hAmount++; if ($(list[i]).attr('data-jqstyle').match('f')) fAmount++; if ($(list[i]).attr('data-jqstyle').match('p')) pAmount++; if ($(list[i]).attr('data-jqstyle').match('r')) rAmount++; } if($('.ehx-show').text() === 'Show') { // If Visited Galleries are hidden, count those as well if ($(list[i]).hasClass('ehx-visited')) { vAmount++; hCount++; } } } if(!setStore.softHide) { $('#hideCount').html('There ' + (hCount > 1 || hCount == 0 ? 'are ' : 'is ') + '<span>' + hCount + ' hidden ' + (hCount > 1 || hCount == 0 ? 'galleries' : 'gallery') + '</span> on this page.'); } else { $('#hideCount').html('There are <span>0 hidden galleries</span> on this page.'); } $('#hideCount > span').prop('title', 'Hidden: ' + hAmount + ' | Visited: ' + vAmount + ' | Filtered: ' + fAmount + ' | Page Limit: ' + pAmount + ' | Rating Limit: ' + rAmount); } /** * Convert the star count of a specified element to a double * @param {Object} el - A specific element within the DOM */ function getStarNumber(el) { var stars = $(el).find('.ir').css('background-position'); switch(stars) { case "0px -1px": return 5; case "0px -21px": return 4.5; case "-16px -1px": return 4; case "-16px -21px": return 3.5; case "-32px -1px": return 3; case "-32px -21px": return 2.5; case "-48px -1px": return 2; case "-48px -21px": return 1.5; case "-64px -1px": return 1; case "-64px -21px": return 0.5; default: return 0; } } /** * Check a specified element through the filters individually, then apply jqstyle tags for CSS * @param {Object} el - A specific element within the DOM */ function filterCheck(el) { if (filterArr.length > 0) { if(filterArr.some(rx => rx.test($(el).find('.glink').text()))) { // Test our gallery name through our regex filters if(!$(el).hasClass('ehx-hidden')) { $(el).addClass('ehx-hidden'); } addStyle($(el), 'f'); } } // Filter our galleries through the star limit filter if (setStore.stFilter && setStore.stLimit > 0) { if (getStarNumber(el) < setStore.stLimit) { if(!$(el).hasClass('ehx-hidden')) { $(el).addClass('ehx-hidden'); } addStyle($(el), 'r'); } } var pages = 0; // Page Count is stored in a lot of different random elements throughout, there are probably better selectors for this if ($('.gl1e').length) { pages = $(el).find('.gl3e > div:nth-child(5)').text().split(' ')[0]; } else if ($('.gl1c').length) { pages = $(el).find('.gl4c > div:nth-child(2)').text().split(' ')[0]; } else if ($('.gl1t').length) { pages = $(el).find('.gl5t > div:nth-child(2) > div:nth-child(2)').text().split(' ')[0]; } else { pages = $(el).find('.gl2m > div:nth-child(2) > div:nth-child(2) > div:nth-child(2) > div:nth-child(2)').text().split(' ')[0]; } if (setStore.pFilter && setStore.pLimit > 0) { if(parseInt(pages) < parseInt(setStore.pLimit)) { if(!$(el).hasClass('ehx-hidden')) { $(el).addClass('ehx-hidden'); } addStyle($(el), 'p'); } } } /** * Adds the specified style flag to a specified element * @param {Object} el - A specific element within the DOM * @param {String} flag - A character to mark an element for JQ Styling CSS rules */ function addStyle(el, flag) { if ($(el).attr('data-jqstyle') != undefined) { $(el).attr('data-jqstyle', $(el).attr('data-jqstyle') + flag); } else { $(el).attr('data-jqstyle', flag); } } /** * Removes the specified style flag from a specified element * @param {Object} el - A specific element within the DOM * @param {string} flag - A JQ Style flag to remove from an element */ function removeStyle(el, flag) { if ($(el).attr('data-jqstyle') != undefined) { $(el).attr('data-jqstyle', $(el).attr('data-jqstyle').replace(flag, '')); } else { $(el).attr('data-jqstyle', ''); } } /** * Toggles a specified element's hidden status * @param {String} tga - Full gallery URL * @param {Object} el - A specific element within the DOM */ function toggleElement(tga, el) { const request = indexedDB.open('ehxvisited', 1); var tgid = tga.split('/')[4] + '.' + tga.split('/')[5]; request.onsuccess = e => { db = e.target.result; var objStore = db.transaction('hidden', 'readwrite').objectStore('hidden'); var openReq = objStore.openCursor(tgid); openReq.onsuccess = e => { var cursor = e.target.result; if (cursor) { // Gallery already exists within our hidden table cursor.delete(); console.log('EhxVisited: Removed ' + tgid + ' from hidden list.'); $(el).removeClass('ehx-hidden'); removeStyle($(el), 'h'); delete hidden.data[tgid]; // Remove gallery listing from our local store of hidden galleries } else { objStore.put({id: tgid}); // Put the gallery into our hidden table console.log('EhxVisited: Added ' + tgid + ' to hidden list.'); $(el).addClass('ehx-hidden'); addStyle($(el), 'h'); hidden.data[tgid] = 1; // Add gallery listing to our local store of hidden galleries } updateGListing(); $('#hLength').text(Object.keys(hidden.data).length); } } } /** * Fill our local gallery listings so we can preform easier operations on the data. * Also set up the majority of our global HTML elements and their functions. */ function populate() { // TODO: Separate the HTML entries from the population portion galleries = JSON.parse('{"data":{}}'); hidden = JSON.parse('{"data":{}}'); const request = indexedDB.open('ehxvisited', 1); request.onupgradeneeded = e => { db = e.target.result; if (!db.objectStoreNames.contains('galleries')) { db.createObjectStore('galleries', {keyPath: 'id'}); } if (!db.objectStoreNames.contains('hidden')) { db.createObjectStore('hidden', {keyPath: 'id'}); } }; request.onsuccess = e => { db = e.target.result; var objStore = db.transaction('galleries', 'readonly').objectStore('galleries'); var openReq = objStore.getAll(); openReq.onsuccess = f => { console.log('EhxVisited: Populated global variables.'); var transform = f.target.result; for (var i = 0; i < transform.length; i++) { galleries.data[transform[i].id] = transform[i].visited; // Force matrix data into array data } var gLength = Object.keys(galleries.data).length var objStore2 = db.transaction('hidden', 'readonly').objectStore('hidden'); var openReq2 = objStore2.getAll(); openReq2.onsuccess = g => { var transform2 = g.target.result; for (i = 0; i < transform2.length; i++) { hidden.data[transform2[i].id] = 1; // Force matrix data into array data } var hLength = Object.keys(hidden.data).length $('#toppane').append('<ehx class="ehx-controls">Galleries visited: <span id="gLength">' + gLength + '</span> ( <span id="ehx-menu-control"></span><a href="javascript:;" class="ehx-settings">Settings</a> )<br/>Hidden Galleries: <span id="hLength">' + hLength + '</span><span id="ehxh-menu-control"></span></ehx>'); if (!setStore.softHide) { $('#ehx-menu-control').append('<a href="javascript:;" class="ehx-show">Hide</a> / '); $('#ehxh-menu-control').append(' ( <a href="javascript:;" class="ehxh-show">Show</a> )'); } $('.ehx-settings').click(e => { e.preventDefault(); settings(); return false; }); $('.ehx-show').click(e => { var disp = $('.ehx-visited.ehx-hidden').css('display'); if ($('.ehx-show').text() === 'Show') { $('.ehx-visited').css({display: ''}); $('.ehx-visited.ehx-hidden').css({display: disp}); } else { $('.ehx-visited').css({display: 'none'}); $('.ehx-visited.ehx-hidden').css({display: disp}); } $('.ehx-show').text((i, t) => { return t === 'Show' ? 'Hide' : 'Show'; }); updateGListing(); setStore.visHide = $('.ehx-show')[0].innerText === 'Show' ? true : false; localStorage.setItem('ehx-settings', JSON.stringify(setStore)); // Update our stored settings }); $('.ehxh-show').click(e => { if ($('.ehxh-show').text() === 'Show') { if ($('.gl1t').length == 0) { // For table view modes $('.ehx-hidden').css({display: $('.ehx-hidden').parent().find('tr').not('.ehx-hidden').css('display')}); // Copy the display CSS of our closest element $('.ehx-visited.ehx-hidden').css({display: $('.ehx-hidden').parent().find('tr').not('.ehx-hidden').css('display')}); } else { // Default display CSS for thumbnail $('.ehx-hidden').css({display: 'flex'}); $('.ehx-visited.ehx-hidden').css({display: 'flex'}); } } else { $('.ehx-hidden').css({display: ''}); $('.ehx-visited.ehx-hidden').css({display: ''}); } $('.ehxh-show').text((i, t) => { // Toggle text return t === 'Show' ? 'Hide' : 'Show'; }); updateGListing(); setStore.hidShow = $('.ehxh-show')[0].innerText === 'Hide' ? true : false; localStorage.setItem('ehx-settings', JSON.stringify(setStore)); }); $('.ehx-controls').append('<br /><span id="hideCount"><span></span></span>'); addCSS(); updateGListing(); } } } } /** * Our main function that does basically everything that we see. * Appends our custom HTML objects to the main page. * Adds CSS to elements based on whether they can be found in the populated local gallery listing. */ function addCSS() { // TODO: Put all the time difference lines in some kind of function to clean this up observer.disconnect(); // Disconnect the observer, or else all the CSS changes will trigger the mutation observer var img_hide = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg=='; var list = $('table.itg>tbody>tr').has('.glhide, .gldown, th'); // Present only in list views var thumb = $('.itg .gl1t'); // Present only in thumbnail view var gid, galleryId, onFavs, d; if (list.length > 0) { if ($('.gl1e').length) { // Extended if ($('h1').text() === 'Favorites') { onFavs = 1; } for (var i = 0; i < list.length; i++) { gid = $(list[i]).find('.gl1e a').attr('href').split('/'); galleryId = gid[4] + '.' + gid[5]; if ($(list[i])[0].children.length === 2 && onFavs) { $(list[i]).append('<td></td>'); } if(galleries.data[galleryId] != undefined) { if (!$(list[i]).hasClass('ehx-visited')) { d = new Date(galleries.data[galleryId]); $(list[i]).addClass('ehx-visited'); if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); if (setStore.repPub) $(list[i]).find('.gl3e div:nth-child(2)').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0')); // Check for fav pages if ($(list[i]).find('.gl3e').children('div').length >= 7) { // Date favourited div is present $(list[i]).find('.gl3e > div:last-child').append('<br><ehx class="ehx-extended-favs">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + '<br>' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')</ehx>'); } else { $(list[i]).find('.gl3e').append('<ehx class="ehx-extended">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + '<br>' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')</ehx>'); } } } else { // Hidden if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited'); if (setStore.repPub) $(list[i]).find('.gl3e div:nth-child(2)').text('Never Visited'); } if(hidden.data[galleryId] != undefined) { if(!$(list[i]).hasClass('ehx-hidden')) { $(list[i]).addClass('ehx-hidden'); } addStyle($(list[i]), 'h'); } if($(list[i])[0].childElementCount < 3) { // Don't append anything if it's already been appended $('<img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; padding: 4px 0px 0px 4px;" title="Show/Hide gallery">').prependTo($(list[i]).find('a').first().parent().parent().parent()).on('click', e => { var el = $(e.currentTarget).parent(); toggleElement($(el).find('a').attr('href'), $(el)); }) } filterCheck($(list[i])); } updateGListing(); } else if ($('.gl1c').length) { // Compact var borderColour = $('.gl1c').first().css('border-top-color'); // Border colour is different between domains if (!pIS && $('table.itg tbody>tr:first-child')[0].children.length < 5) { // Apend our table columns to the table head $('table.itg tbody>tr:first-child th:nth-child(4)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">✖</th>'); if (setStore.minAdd) { $('table.itg tbody>tr:first-child th:nth-child(2)').after('<th>Visited</th>'); } if (setStore.repPub) { $('table.itg tbody>tr:first-child')[0].children[1].innerText = 'Visited' } pIS = true; } if ($('h1').text() === 'Favorites') { onFavs = 1; } for (i = 1; i < list.length; i++) { gid = $(list[i]).find('.glname a').attr('href').split('/'); galleryId = gid[4] + '.' + gid[5]; if ($(list[i])[0].children.length === 4 || $(list[i])[0].children.length === 5 && onFavs) { if ($(list[i])[0].children.length === 4 && onFavs) { $(list[i]).append('<td></td>'); } if(galleries.data[galleryId] != undefined) { d = new Date(galleries.data[galleryId]); $(list[i]).addClass('ehx-visited'); if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0')); if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); if (setStore.minAdd) $(list[i]).children('.gl2c').after('<td class="ehx-compact" style="border-color: ' + borderColour + ';"><ehx>' + timeDifference(galleries.data[galleryId], true) + '<br>(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')<br>' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx></td>'); } else { if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text('Never Visited'); if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited'); if (setStore.minAdd) $(list[i]).children('.gl2c').after('<td class="ehx-compact" style="border-color:' + borderColour + ';"></td>'); } } else { if (galleries.data[galleryId] != undefined) { d = new Date(galleries.data[galleryId]); $(list[i]).addClass('ehx-visited'); if (setStore.repPub) $(list[i]).find('.gl2c div:nth-child(3)').children().first().text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0')); if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); if (setStore.minAdd) $(list[i]).find('.ehx-compact').html('<ehx>' + timeDifference(galleries.data[galleryId], true) + '<br>(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ')<br>' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx>'); } } if(hidden.data[galleryId] != undefined) { if(!$(list[i]).hasClass('ehx-hidden')) { $(list[i]).addClass('ehx-hidden'); } addStyle($(list[i]), 'h'); } if($(list[i])[0].childElementCount < 5 || ($(list[i])[0].childElementCount < 6 && setStore.minAdd)) { // Don't append anything if it's already been appended $('<td class="hideContainer" style="border-bottom: 1px solid #6f6f6f4d; border-top: 1px solid #6f6f6f4d;"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; vertical-align: center;" title="Show/Hide gallery"></td>').appendTo($(list[i]).find('a').first().closest('tr')).on('click', e => { var el = $(e.currentTarget).closest('tr'); toggleElement($(el).find($('.gl3c > a')).attr('href'), $(el)); }) } filterCheck($(list[i])); } updateGListing(); } else { // Minimal if(!pIS) { // Append our table columns to the table head $('table.itg tbody>tr:first-child th:nth-child(6)').after('<th style="text-align: center;" title="EhxVisited: Click to Show/Hide">✖</th>'); if (setStore.minAdd) { $('table.itg tbody>tr:first-child th:nth-child(2)').after('<th title="EhxVisited: Hover for timestamps" style="text-align: center;">\uD83D\uDC41</th>'); } if (setStore.repPub) { $('table.itg tbody>tr:first-child')[0].children[1].innerText = 'Visited' } pIS = true; } if ($('h1').text() === 'Favorites') { onFavs = 1; } for (i = 1; i < list.length; i++) { gid = $(list[i]).find('.glname a').attr('href').split('/'); galleryId = gid[4] + '.' + gid[5]; if(hidden.data[galleryId] != undefined) { if(!$(list[i]).hasClass('ehx-hidden')) { $(list[i]).addClass('ehx-hidden'); } addStyle($(list[i]), 'h'); } if ($(list[i])[0].children.length === 6 || $(list[i])[0].children.length === 7 && onFavs) { if ($(list[i])[0].children.length === 6 && onFavs) { $(list[i]).append('<td></td>'); } if (galleries.data[galleryId] != undefined) { d = new Date(galleries.data[galleryId]); $(list[i]).addClass('ehx-visited'); if (setStore.minAdd) { // Append viewed column if (setStore.minShow) { // Show text in appended column $(list[i]).children('.gl2m').after('<td class="ehx-minimal-text"><ehx title="(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '">' + timeDifference(galleries.data[galleryId], true) + '</ehx></td>'); } else { // Show icon in appended column $(list[i]).children('.gl2m').after('<td class="ehx-minimal" title="EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '"><ehx>\uD83D\uDC41</ehx></td>'); } } if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); if (setStore.repPub) $(list[i]).find('.gl2m').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0')); } else { // Not viewed if (setStore.minAdd) $(list[i]).children('.gl2m').after('<td class="' + (setStore.minShow ? 'ehx-minimal-text' : 'ehx-minimal') + '"></td>'); // Only append another table cell to the row if (setStore.repPub) $(list[i]).find('.gl2m').text('Never Visited'); if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'Never Visited'); } if($(list[i])[0].childElementCount < 8 || ($(list[i])[0].childElementCount < 8 && !setStore.minAdd)) { // Don't append anything if it's already been appended // I'll have to look at this conditional again, seems redundant $('<td class="hideContainer"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; vertical-align: middle;" title="Show/Hide gallery"></td>').appendTo($(list[i]).find('a').first().closest('tr')).on('click', e => { var el = $(e.currentTarget).closest('tr'); toggleElement($(el).find($('.gl3m > a')).attr('href'), $(el)); }) } } else { if (galleries.data[galleryId] != undefined) { d = new Date(galleries.data[galleryId]); $(list[i]).addClass('ehx-visited'); if (setStore.minAdd) { if (setStore.minShow) { $(list[i]).find('.ehx-minimal-text').html('<ehx title="(' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString().substr(2) + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '">' + timeDifference(galleries.data[galleryId], true) + '</ehx>'); } else { $(list[i]).find('.ehx-minimal').html('<ehx>\uD83D\uDC41</ehx>'); $(list[i]).find('.ehx-minimal').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); } } if (setStore.cssTT) $(list[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); if (setStore.repPub) $(list[i]).find('.gl2m').text(d.getFullYear().toString() + '\u2011' + ('0' + (d.getMonth() + 1)).slice(-2) + '\u2011' + d.getDate() + ' ' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0')); } } filterCheck($(list[i])); } updateGListing(); } } else if (thumb.length > 0) { // Thumbnail for (i = 0; i < thumb.length; i++) { gid = $(thumb[i]).find('.gl3t a').attr('href').split('/'); galleryId = gid[4] + '.' + gid[5]; if(galleries.data[galleryId] != undefined) { if (!$(thumb[i]).hasClass('ehx-visited')) { d = new Date(galleries.data[galleryId]); $(thumb[i]).addClass('ehx-visited'); if (setStore.cssTT) { $(thumb[i]).find('.glname').attr('title', 'EhxVisited: ' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate()); } $(thumb[i]).children('.gl5t').after('<ehx class="ehx-thumbnail">\uD83D\uDC41' + timeDifference(galleries.data[galleryId]) + ' (' + d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0') + ') ' + d.getFullYear().toString() + '\u2011' + (d.getMonth() + 1) + '\u2011' + d.getDate() + '</ehx>'); } } else { if (setStore.cssTT) { $(thumb[i]).find('.glname').attr('title', 'Never Visited'); } } if(hidden.data[galleryId] != undefined) { if(!$(thumb[i]).hasClass('ehx-hidden')) { $(thumb[i]).addClass('ehx-hidden'); } addStyle($(thumb[i]), 'h'); } if($(thumb[i]).find('.gl5t').children().length < 3) { // Don't append anything if it's already been appended $('<div class="hideContainer"><img class="imgHide" src="' + img_hide + '" style="cursor: pointer !important; position: absolute; bottom: 3px; left: 2px;" title="Show/Hide gallery"></div>').appendTo($(thumb[i]).find('.gl5t')).on('click', e => { var el = $(e.currentTarget).parent().parent(); toggleElement($(el).find('a').attr('href'), $(el)); }) } filterCheck($(thumb[i])); } updateGListing(); } else { console.log('EhxVisited: Something went wrong or an invalid view'); // This happens when you use empty() on itg, which can happen with other scripts } if (setStore.visHide) { $('.ehx-visited').css({display: 'none'}); $('.ehx-show').text('Show'); } if (setStore.hidShow) { if ($('.ehx-hidden').length < 25) { $('.ehx-hidden').css({display: $('.ehx-hidden').siblings().not('.ehx-hidden').css('display')}) } // Make sure there are elements on the page else { // Unless you're an idiot and hid everything on the page if ($('.gl1t').length) { $('.ehx-hidden').css({display: 'flex'}); } // Use the default values else { $('.ehx-hidden').css({display: 'table-row'}); } } $('.ehxh-show').text('Hide'); } observer.observe($('.itg').get(0), { // Reconnect the observer for changes childList: true, subtree: true }); } /** * Get time difference in words * @param {Date} previous - Previous date to compare against Date.now() * @param {Boolean} abbreviate - Should the text string have abbreviatated text */ function timeDifference(previous, abbreviate) { var msPerMinute = 60 * 1000; var msPerHour = msPerMinute * 60; var msPerDay = msPerHour * 24; var msPerMonth = msPerDay * 30; var msPerYear = msPerDay * 365; var elapsed = Date.now() - previous; if (elapsed < msPerMinute) { return Math.round(elapsed / 1000) + ((typeof abbreviate !== 'undefined') ? ' 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 */ function displayAlert(message, timeout) { var alert = $(`<div class="notice">EhxVisited: ` + message + `</div>`); $(alert).hide().appendTo('body').fadeIn(1000); setTimeout(e => { $('.notice').fadeOut(1000, f => { $('.notice').remove(); }); }, timeout); } /** * Fills a text area with formatted gallery data for export * @param {IndexedDB Matrix} items - Raw IndexedDB getAll output */ function ehxExport(items) { var data = ''; for (var i in items) { data += items[i].id + ':' + items[i].visited + ';'; } $('#exportGalleries').val(''); // Clear text area $('#exportGalleries').val(data); // Fill with formatted data $('#exportGalleries').select(); } $('.itg').on('mousedown', 'a', e => { if (e.which === 3) return; // Ignore right-clicks if (e.currentTarget.href.split('/')[3] === 'g') { galleries.data[e.currentTarget.href.split('/')[4] + '.' + e.currentTarget.href.split('/')[5]] = Date.now(); $('#gLength').text(Object.keys(galleries.data).length); addCSS(); } }); /** * Import user data into our indexedDB * @param {String} store - An object store within the indexedDB * @param {JSON Array} items - String of exported data to import */ function ehxImport(store, items) { var objStore2 = db.transaction(store, 'readwrite').objectStore(store); var count = 0, sp = ''; sp = items.split(';'); sp = sp.filter(Boolean); // Filter out any null ('') entries insertNext(); /** * Push entries into the specified indexedDB store */ function insertNext() { if (count < sp.length) { var str = sp[count].split(':'); objStore2.put({id: str[0], visited: parseInt(str[1])}).onsuccess = insertNext; // Update the record if it's there, or add it if it's not, then continue ++count; } else { displayAlert('Imported ' + count + ' entries', 5000); console.log('EhxVisited: Merge Completed'); $('#importGalleries').val(''); populate(); } } } /** * Open the Settings menu and set up all necessary menu functions */ function settings() { const req = indexedDB.open('ehxvisited', 1); req.onsuccess = e => { if (db == null) db = e.target.result; var objStore = db.transaction('galleries', 'readwrite').objectStore('galleries'); var openReq = objStore.getAll(); openReq.onsuccess = e => { var len = e.target.result; // There's probably a much easier way to do this, or at least a nicer looking, more technical way var container = $(` <div class="overlay"> <div class="settings"> <nav class="topNav"> <span style="float: left; margin-left: 3px; font-weight: lighter; opacity: 0.5; -webkit-opacity: 0.5;">All Applied Settings Will Take Effect On Close</span> <div> <div class="mencon"> <button class="menu">Export</button> <div class="dropdown"> <a id="ehx-export" href="javascript:;">Export Galleries</a> <a id="ehxh-export" href="javascript:;">Export Hidden Galleries</a> </div> </div> <div class="mencon"> <button class="menu">Import</button> <div class="dropdown"> <a id="ehx-import" href="javascript:;">Import Galleries</a> <a id="ehxh-import" href="javascript:;">Import Hidden Galleries</a> </div> </div> <a id="settings-close" href="javascript:;">🞫</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 class="ehx-clear">Clear Data</button> </div> </h3> <textarea id="visited" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.visible + `</textarea> <h3>Hidden Galleries <div class="control sControls" id="hideControls"> <button id="resH">Reset CSS</button> <button class="ehxh-clear">Clear Data</button> </div> </h3> <textarea id="hidden" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.hidden + `</textarea> <div class="suboptions2"> <button class="collapsible">Filtered Galleries</button> <div class="content"> <textarea id="filtered" class="field" spellcheck="false">` + cssA.filter + `</textarea> <div class="control sControls"> <button id="resF">Reset CSS</button> </div> </div> <button class="collapsible">Page Filtered</button> <div class="content"> <textarea id="page" class="field" spellcheck="false"placeholder="Insert CSS">` + cssA.page + `</textarea> <div class="control sControls"> <button id="resP">Reset CSS</button> </div> </div> <button class="collapsible">Rating Filtered</button> <div class="content"> <textarea id="rating" class="field" spellcheck="false" placeholder="Insert CSS">` + cssA.rating + `</textarea> <div class="control sControls"> <button id="resR">Reset CSS</button> </div> </div> </div> </fieldset> <fieldset> <legend>Filters</legend> Use one <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">regular expression</a> per line to filter out matching galleries. <ul style="margin: 3px 0px; padding-left: 30px;"> <li>E.G. <code>Ongoing</code> will filter out every gallery with <code>ongoing</code>, case-insensitive, in the title. <code>\\[Digital\\]</code> will filter out every gallery with <code>[Digital]</code>, case-insensitive, in the title.</li> <li>Lines starting with <code>#</code> will be ignored.</li> </ul> <textarea id="galFilter">` + filters + `</textarea> <div> <label> <input type="checkbox" id="pFilt" ` + (setStore.pFilter ? `checked` : ``) + `>Page Limit </label> <span>: Filter out any gallery with pages less than: <input id="pLim" type="number" min="1" value="` + setStore.pLimit + `" ` + (setStore.pFilter ? `` : `disabled`) + `/> </span> </div> <div> <label> <input type="checkbox" id="stFilt" ` + (setStore.stFilter ? `checked` : ``) + `>Minimum Rating </label> <span>: Filter out any gallery with a rating less than: <select id="stLim" ` + (setStore.stFilter ? `` : `disabled`) + `> <option>5</option> <option>4.5</option> <option>4</option> <option>3.5</option> <option>3</option> <option>2.5</option> <option>2</option> <option>1.5</option> <option>1</option> </select> </span> </div> </fieldset> </section> <section class="inactive"> <fieldset style="padding-bottom: 2px; min-height: 474px;"> <legend id="importTitle">Import Galleries</legend> <textarea id="importGalleries"></textarea> <div class="control" style="margin-top: 2px; margin-bottom: 4px;"> <button class="cancel">Cancel</button> <button id="importConfirm">Import</button> </div> </fieldset> </section> <section class="inactive"> <fieldset style="padding-bottom: 2px; min-height: 474px;"> <legend id="exportTitle">Export Galleries</legend> <textarea id="exportGalleries"></textarea> <div class="control" style="margin-top: 2px; margin-bottom: 4px;"> <button class="cancel">Cancel</button> <button id="exportCopy">Copy</button> </div> </fieldset> </section> </div> <div class="applyContainer"> <div class="control" style="padding-right: 5px;"> <button id="apply">Apply</button> </div> </div> </div> </div>`); $('body').append(container); if (!$('#minAdd').prop('checked')) { $('#minShow').prop('disabled', true); } $('#resV').click(e => { $('#visited').val('box-shadow: inset 0 0 0 500px rgba(2, 129, 255, .2) !important;'); }); // Default Values $('#resH').click(e => { $('#hidden').val('box-shadow: inset 0 0 0 500px rgba(255, 40, 0, .2) !important;'); }); $('#resF').click(e => { $('#filtered').val('box-shadow: inset 0 0 0 500px rgba(200, 0, 100, .2) !important;'); }); $('#resP').click(e => { $('#page').val('box-shadow: inset 0 0 0 500px rgba(0, 0, 180, .2) !important;'); }); $('#resR').click(e => { $('#rating').val('box-shadow: inset 0 0 0 500px rgba(180, 80, 60, .2) !important;'); }); $('#stLim').val(setStore.stLimit); $(document).on('change', 'input', e => { // Put the change listener on document since I suck at event propogation and bubbling if ($('#minAdd').prop('checked')) { $('#minShow').prop('disabled', false); } else { $('#minShow').prop('disabled', true); } if ($('#pFilt').prop('checked')) { $('#pLim').prop('disabled', false); } else { $('#pLim').prop('disabled', true); } if ($('#stFilt').prop('checked')) { $('#stLim').prop('disabled', false); } else { $('#stLim').prop('disabled', true); } }); $('#settings-close').click(e => location.reload()); // The price I have to pay for being bad at doing things dynamically $('body').click(e => { if (e.target.className == "overlay") { // Exit if settings menu isn't clicked location.reload(); } else if (e.target.className != 'show' && e.target.className != 'menu') { $('.show').removeClass('show'); } }); $('#apply').click(e => applySettings()); /** * Parse our HTML options into a temporary JSON array and then stringify it into localStorage */ function applySettings() { var tempSto = { "softHide": $('#softHide').prop('checked'), "minAdd": $('#minAdd').prop('checked'), "minShow": $('#minShow').prop('checked'), "cssTT": $('#cssTT').prop('checked'), "repPub": $('#repPub').prop('checked'), "visHide": $('.ehx-show').text() === "Show" ? true : false, "hidShow": $('.ehxh-show').text() === "Hide" ? true : false, "pFilter": $('#pFilt').prop('checked'), "pLimit": $('#pFilt').prop('checked') ? $('#pLim').val() : "0", "stFilter": $('#stFilt').prop('checked'), "stLimit": $('#stFilt').prop('checked') ? $('#stLim option:selected').text() : "0", "titleShow": $('#titleShow').prop('checked') } localStorage.setItem('ehx-settings', JSON.stringify(tempSto)); // Write settings to localStorage var tempCss = { "visible": $('#visited').val(), "hidden": $('#hidden').val(), "filter": $('#filtered').val(), "page": $('#page').val(), "rating": $('#rating').val() } localStorage.setItem('ehx-css', JSON.stringify(tempCss)); localStorage.setItem('ehx-filters', $('#galFilter').val().replace(/^\s*[\r\n]/gm, '')); // Remove null entries because bad things happen if they're there displayAlert('Applied Current Settings', 5000); } $('.collapsible').click(e => { // Expand our custom filtering CSS boxes if ($('.active').length && !$('.active').is(e.target)) { // If a menu is open and it isn't the one we're clicking, close it $('.active').next().css('max-height', ''); $('.active').toggleClass('active'); } e.target.classList.toggle('active'); var content = e.target.nextElementSibling; if (content.style.maxHeight){ content.style.maxHeight = null; } else { content.style.maxHeight = '500px'; } }); $('#ehx-import').click(e => { $('.section-container section').addClass('inactive'); $('.section-container section:nth-child(2)').removeClass('inactive'); $('#importTitle').text('Import Galleries'); $('#importGalleries').val(''); }); $('#ehxh-import').click(e => { $('.section-container section').addClass('inactive'); $('.section-container section:nth-child(2)').removeClass('inactive'); $('#importTitle').text('Import Hidden Galleries'); $('#importGalleries').val(''); }); $('#importConfirm').click(e => ehxImport($('#importTitle').text().search(/hidden/i) != -1 ? 'hidden' : 'galleries', $('#importGalleries').val().replace(/^\s*[\r\n]/gm, ''))); $('.cancel').click(e => { $('.section-container section').addClass('inactive'); $('.section-container section:first-child').removeClass('inactive'); }); $('#exportCopy').click(e => { $('#exportGalleries').select(); document.execCommand('copy'); displayAlert('Copied Text To Clipboard', 5000); }); $('#ehx-export').click(e => { $('.section-container section').addClass('inactive'); $('.section-container section:nth-child(3)').removeClass('inactive'); $('#exportTitle').text('Exported Galleries'); ehxExport(len); }); $('#ehxh-export').click(e => { var objStore2 = db.transaction('hidden', 'readwrite').objectStore('hidden'); var openReq = objStore2.getAll(); openReq.onsuccess = e => { $('.section-container section').addClass('inactive'); $('.section-container section:nth-child(3)').removeClass('inactive'); $('#exportTitle').text('Exported Hidden Galleries'); ehxExport(e.target.result); } }); $('.ehx-clear').click(e => { if (!ehxClearConfirm) { // Make sure to double check before deleting ehxClearConfirm = 1; $('.ehx-clear').append(': Are you sure?'); } else { var objStore2 = db.transaction('galleries', 'readwrite').objectStore('galleries'); var openReq = objStore2.clear(); openReq.onsuccess = e => { displayAlert('Cleared all entries', 5000); $('.ehx-clear').text('Clear Data'); } } }); $('.ehxh-clear').click(e => { var objStore2 = db.transaction('hidden', 'readwrite').objectStore('hidden'); var openReq = objStore2.getAll(); openReq.onsuccess = e => { if (!ehxhClearConfirm) { // Make sure to double check before deleting ehxhClearConfirm = 1; $('.ehxh-clear').append(': Are you sure?'); } else { var objStore3 = db.transaction('hidden', 'readwrite').objectStore('hidden'); var openReq = objStore3.clear(); openReq.onsuccess = e => { displayAlert('Cleared all entries', 5000); $('.ehxh-clear').text('Clear Data'); } } } }); // Fancy function to make sure there's not more than one top menu item open $('.menu').click(e => { if ($('.show').length) { if ($('.show').prev().is(e.target)) { $(e.target).next().toggleClass('show'); } else { $('.show').removeClass('show'); $(e.target).next().toggleClass('show'); } } else { $(e.target).next().toggleClass('show'); } }); } } } // The giant CSS block $(`<link rel="stylesheet" media="screen" href="https://fontlibrary.org/face/symbola" type="text/css"/> <style data-jqstyle='ehxVisited'> #hideCount > span { border-bottom: 1px dotted currentColor; } #importGalleries, #exportGalleries { min-height: 420px; } #settings-close { text-decoration: none; position: absolute; top: -2px; right: 1.5px; font-size: 1.4em; } #visControls { top: -6px; } ` + (setStore.titleShow ? `div.gl4t:hover { overflow: visible; z-index: 3; position: relative; background: rgba(0, 0, 0, 0.5); height: auto; }` : ``) + ` ehx { font-family:` + $('body').css('font-family') + `, arial, symbola, SymbolaRegular; } table.itg > tbody > tr.ehx-visited, .gl1t.ehx-visited { ` + cssA.visible + ` } input[type="checkbox"] { -webkit-appearance: none; border: 1px solid #F1F1F1BB; padding: 5px; top: 4px; background-color: transparent; } input[type="checkbox"]:checked:after { content: '\\2714'; position: absolute; top: -8px; left: 1px; font-size: 1.1em; } input[type="checkbox"]:focus { outline: none; } input[type="checkbox"]:hover { cursor: pointer; } nav > div { text-align: right; margin-right: 30px; } nav > div button { border: none !important; padding: 1px 20px 1px 10px !important; position: relative; } table.itg > tbody > tr.ehx-visited.ehx-hidden, .gl1t.ehx-visited.ehx-hidden { ` + cssA.hidden + ` } .active:after { content: '\\2212' !important; } .applyContainer { padding-top: 5px; padding-right: 8px; border-top: 1px solid threedface; width: 100%; position: relative; left: -4px; } .branch { position: absolute; left: 10px; top: 1px; margin-left: -3px; } .collapsible { cursor: pointer; width: 100%; border: 0; outline: none; text-align: left; font-size: 1.25em; background-color: rgba(0, 0, 0, 0); color: inherit; font-weight: bold; padding:5px 3px; position: relative; } .collapsible:after { content: '\\002B'; font-weight:bold; float:right; margin-right:5px; } .collapsible:before { content: ''; position: absolute; padding: 4px; border-bottom: 1px solid threedface; border-left: 1px solid threedface; top: 5px; left: -11px; } .collapsible:hover { background-color: rgba(255, 255, 255, 0.1); } .content { max-height: 0; overflow: hidden; transition: all .2s ease-in-out; border-bottom: 1px solid threedface; } .content button { margin-top: 3px; margin-right: 10px; } .control { position: relative; float: right; right: -5px; } .dropdown { display: none; position: absolute; z-index: 999; min-width: 150px; padding: 2px; border-radius: 1px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); right: 0px; border: 1px solid threedface; background: ` + $('.ido').css('background') + `; background: ` + $('.ido').css('backgroundColor') + `; } .dropdown a { display: block; text-decoration: none; padding-right:2px; } .dropdown a:hover { background: rgba(255,255,255,0.2); } .ehx-compact { border-style: solid; border-width: 1px 0; text-align: center; } .ehx-controls { padding: 3px 1px; text-align: center; display: block; } .ehx-exported-data { display: block; } .ehx-extended { width: 120px; position: absolute; left: 3px; top: 172px; text-align: center; font-size: 8pt; line-height: 1.5; } .ehx-extended-favs { padding: 3px 1px; display: block; line-height: 1.5; } .ehx-hidden { ` + cssD + cssA.hidden + ` } .ehx-hidden[data-jqstyle*="f"] {` + cssA.filter + `} .ehx-hidden[data-jqstyle*="p"] {` + cssA.page + `} .ehx-hidden[data-jqstyle*="r"] {` + cssA.rating + `} .ehx-minimal { border-left: 1px solid #6f6f6f4d; } .ehx-minimal-text { text-align: center; display: block; } .ehx-thumbnail { display: block; text-align: center; margin: 3px 0 5px; line-height: 12px; } .ehx-visited .gl3e { min-height: 206px; } .ehx-visited .gl4e { min-height: 264px !important; } .gl2c { width: 115px; } .inactive { display: none; } .mencon { display: inline-block; position: relative; } .menu:after { content: '\\2335'; position: absolute; right: 5px; bottom: 1px; } .notice { position: fixed; width: 500px; height: 20px; top: 20px; left: 50%; transform: translate(-50%, 0px); background: rgba(70, 130, 180, 0.8); font-size: 1.2em; font-weight: bold; color: white; padding-top: 5px; border-radius: 8px; z-index: 999; } .overlay { background: rgba(0,0,0,0.5); display: -webkit-flex; display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 100; font-size: 9pt; } .overlay button:not(.collapsible) { background-color: transparent; border-radius: 6px; border: 1px solid threedface; cursor: pointer; font-weight: bold; padding: 3px 20px; text-decoration: none; color: inherit; margin-left: 5px; } .overlay button:not(.collapsible):hover { background-color: rgba(255, 255, 255, 0.1); } .overlay button:not(.collapsible):focus { outline: none; } .sControls { top: -3px; } .section-container { text-align: left; overflow: auto; margin-bottom: 5px; padding-bottom: 5px; } .section-container textarea:disabled, .section-container input:disabled, .section-container select:disabled { opacity: 0.6; -webkit-opacity: 0.6; } .section-container input[type="number"] { border: 1px solid #8d8d8d; margin-left:0px; text-align: center; width: 50px; } .section-container select { margin-left: 0px; } .section-container code { color: #000; background-color: #FFF; } .section-container fieldset { padding-right: 18px; } .settings { background: ` + $('.ido').css('background') + `; background: ` + $('.ido').css('backgroundColor') + `; box-sizing: border-box; height: 555px; max-height: 100%; width: 900px; max-width: 100%; margin: auto; padding: 5px; display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.5); } .settings nav { text-align: right; padding-bottom: 5px; font-weight: bold; position: relative; } .settings legend { font-size: 10pt; font-weight: bold; } .settings label { font-weight: bold; text-decoration: underline; cursor: pointer; } .settings h3 { margin: 3px; position: relative; } .settings input { vertical-align: -1px; } .settings textarea { width: 100%; height: 50px; resize: vertical; } .show { display:block } .suboptions { position: relative; } .suboptions > div { position: relative; padding-left: 1.4em; } .suboptions2 { margin-left: 4px; padding-left: 10px; margin-right: -9px; } </style>`).appendTo('head'); })();