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