NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name ExHentai/E-Hentai Free Tagsets // @description Unlimited locally stored tagsets for free // @namespace Violentmonkey Scripts // @match https://exhentai.org/mytags // @match https://e-hentai.org/mytags // @version 1.0 // @author shlsdv // @icon https://e-hentai.org/favicon.ico // @grant GM_getValue // @grant GM_setValue // @license MIT // ==/UserScript== const tagsets = GM_getValue('tagsets') || {}; let newSelect; let pendingSaves = new Set(); function usertagSave(a) { let id = a; pendingSaves.add(id); void 0 == usertag_xhr && ( usertag_xhr = new XMLHttpRequest, a = { method: 'setusertag', apiuid, apikey, tagid: a, tagwatch: document.getElementById('tagwatch_' + a).checked ? 1 : 0, taghide: document.getElementById('taghide_' + a).checked ? 1 : 0, tagcolor: document.getElementById('tagcolor_' + a).value, tagweight: document.getElementById('tagweight_' + a).value }, api_call(usertag_xhr, a, () => usertagCallback(id)) ) } function usertagCallback(id) { var a = api_response(usertag_xhr); 0 != a && ( void 0 != a.error ? alert('Could not save tag: ' + a.error) : document.getElementById('selector_' + a.tagid).innerHTML = '<label class="lc"><input type="checkbox" name="modify_usertags[]" value="' + a.tagid + '" /><span></span></label>', usertag_xhr = void 0 ) pendingSaves.delete(id); } function getTagVals(div) { const tagpreviewDiv = div.querySelector('div[id*="tagpreview"]'); const tagwatchInput = div.querySelector('input[id*="tagwatch"]'); const taghideInput = div.querySelector('input[id*="taghide"]'); const tagweightInput = div.querySelector('input[id*="tagweight"]'); const tagcolorInput = div.querySelector('input[id*="tagcolor"]'); const dict = {} const title = tagpreviewDiv ? tagpreviewDiv.getAttribute('title') : ''; const id = div.id.split('_').length > 1 ? parseInt(div.id.split('_')[1]) : 0; dict['watched'] = tagwatchInput ? tagwatchInput.checked : false; dict['hidden'] = taghideInput ? taghideInput.checked : false; dict['weight'] = tagweightInput ? parseInt(tagweightInput.value) || 0 : 0; dict['color'] = tagcolorInput ? tagcolorInput.value : ''; const inputs = [tagwatchInput, taghideInput, tagcolorInput, tagweightInput]; return [title, id, dict, inputs]; } function getCurrentSet() { const dataDict = {}; document.querySelectorAll('div[id*="usertag"]').forEach(usertagDiv => { const [title, id, vals, _] = getTagVals(usertagDiv); if (title != '') { dataDict[title] = vals; } }); return dataDict; } function saveCurrentSet(name) { if (!name) { return; } const dict = getCurrentSet(); tagsets[name] = dict; GM_setValue('tagsets', tagsets); populateSelect(); newSelect.value = name; alert(`Saved tagset '${name}' to script storage!`); } function loadSavedSet(key, save=true) { if (!(key in tagsets)) { return; } const dict = tagsets[key]; const usertagDivs = document.querySelectorAll('div[id*="usertag"]'); (async () => { for (const usertagDiv of usertagDivs) { const [title, id, _, inputs] = getTagVals(usertagDiv); const [tagwatchInput, taghideInput, tagcolorInput, tagweightInput] = inputs; if (!id || !(title in dict)) { continue; } const [watch, hide, color, weight] = [dict[title]['watched'], dict[title]['hidden'], dict[title]['color'], dict[title]['weight']]; const vals1 = [tagwatchInput.checked, taghideInput.checked, tagcolorInput.value, parseInt(tagweightInput.value)]; const changed = ![watch, hide, color, weight].every((x, i) => x === vals1[i]); const saveInput = usertagDiv.querySelector('input[id*="tagsave"]'); if (!changed && !saveInput) { continue; } while (pendingSaves.size > 0) { await new Promise(resolve => setTimeout(resolve, 100)); } tagwatchInput.click(); tagwatchInput.click(); tagwatchInput.checked = watch; taghideInput.checked = hide; tagcolorInput.value = color; update_tagcolor(id, tagcolorInput.value, color.replace(/^#*/, "")); tagweightInput.value = weight; if (save) { usertagSave(id); } //console.log(`${title}, ${id}, ${watch}, ${hide}, ${weight}`); } if (save) { setTimeout(() => alert("Loaded!"), 150); } })(); } function deleteTagset(key) { if (!(key in tagsets)) { return; } delete tagsets[key]; GM_setValue('tagsets', tagsets); populateSelect(); newSelect.value = 'None'; } function populateSelect() { newSelect.innerHTML = ''; for (const key in tagsets) { const option = document.createElement('option'); option.value = key; option.text = key; newSelect.appendChild(option); } } (function() { const newDiv = document.createElement('div'); newDiv.style.paddingLeft = '196px'; newDiv.style.paddingTop = '8px'; const newButton = document.createElement('input'); newButton.type = 'button'; newButton.value = 'New'; newButton.addEventListener('click', () => saveCurrentSet(prompt("Enter tagset name:"))); newDiv.appendChild(newButton); const inputButton = document.createElement('input'); inputButton.type = 'button'; inputButton.value = 'Save'; inputButton.addEventListener('click', () => { let userInput = newSelect.value && newSelect.value in tagsets ? newSelect.value : prompt("Enter tagset name:"); saveCurrentSet(userInput); }); newDiv.appendChild(inputButton); const delBtn = document.createElement('input'); delBtn.type = 'button'; delBtn.value = 'Delete'; delBtn.addEventListener('click', () => deleteTagset(newSelect.value)); newDiv.appendChild(delBtn); newSelect = document.createElement('select'); populateSelect(); newSelect.value = 'None'; newSelect.addEventListener('change', () => loadSavedSet(newSelect.value, false)); newDiv.appendChild(newSelect); const loadButton = document.createElement('input'); loadButton.type = 'button'; loadButton.value = 'Apply'; loadButton.addEventListener('click', () => loadSavedSet(newSelect.value)); newDiv.appendChild(loadButton); const tagsetForm = document.getElementById('tagset_form'); tagsetForm.insertAdjacentElement('afterend', newDiv); })();