// ==UserScript==
// @name f95zone tweaks
// @namespace f95zone tweaks
// @description f95zone exclude tags and min like filter.
// @match https://f95zone.to/sam/latest_alpha/
// @version 1.4.1
// @author 3xd_Tango
// @require https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@2,npm/@violentmonkey/[email protected]
// @require https://cdn.jsdelivr.net/npm/lodash@4/lodash.min.js
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @license GNU GPLv3
// ==/UserScript==
(function () {
'use strict';
var css_248z = ".customTags{border-radius:2px;box-shadow:0 0 0 0 transparent,0 1px 1px 0 rgba(0,0,0,.15),0 1px 2px 0 rgba(0,0,0,.15);display:inline-block;font-size:.8em;line-height:1.8em;margin:4px 4px 0 0;padding:0 6px;text-shadow:1px 1px 2px rgba(0,0,0,.75)}.selected-tags-wrap span:hover{background-color:#ba4545}.selected-tags-wrap span{background-color:#181a1d;border-radius:3px;cursor:pointer;display:inline-block;font-size:.9em;line-height:24px;margin:4px 4px 0 0;padding:0 7px;transition:.2s}.input-tag{left:0;opacity:1;position:relative;width:127px}.border-gradient{border:3px solid;border-image-slice:1}.flxgrow{flex-grow:1}";
const likes = GM_getValue('likes', [['incest', '#ff0000', '30'], ['harem', '#0011ff', '254'], ['futa/trans', '#EE82EE', '191'], ['loli', '#00f5ffff', '639'], ['futa/trans protagonist', '#EE82EE', '2255']]);
const dislikes = GM_getValue('dislikes', [['female protagonist', '392'], ['text based', '522'], ['mind control', '111']]);
const totalTags = GM_getValue('tags', [['2d game', '2214'], ['2dcg', '1507'], ['3d game', '1434'], ['3dcg', '107'], ['adventure', '162'], ['ahegao', '916'], ['anal sex', '2241'], ['animated', '783'], ['bdsm', '264'], ['bestiality', '105'], ['big ass', '817'], ['big tits', '130'], ['blackmail', '339'], ['bukkake', '216'], ['censored', '2247'], ['character creation', '2246'], ['cheating', '924'], ['combat', '550'], ['corruption', '103'], ['cosplay', '606'], ['creampie', '278'], ['dating sim', '348'], ['dilf', '1407'], ['drugs', '2217'], ['dystopian setting', '2249'], ['exhibitionism', '384'], ['fantasy', '179'], ['female domination', '2252'], ['female protagonist', '392'], ['footjob', '553'], ['furry', '382'], ['futa/trans', '191'], ['futa/trans protagonist', '2255'], ['gay', '360'], ['graphic violence', '728'], ['groping', '535'], ['group sex', '498'], ['handjob', '259'], ['harem', '254'], ['horror', '708'], ['humiliation', '871'], ['humor', '361'], ['incest', '30'], ['internal view', '1483'], ['interracial', '894'], ['japanese game', '736'], ['kinetic novel', '1111'], ['lactation', '290'], ['lesbian', '181'], ['loli', '639'], ['male domination', '174'], ['male protagonist', '173'], ['management', '449'], ['masturbation', '176'], ['milf', '75'], ['mind control', '111'], ['mobile game', '2229'], ['monster', '182'], ['monster girl', '394'], ['multiple endings', '322'], ['multiple penetration', '1556'], ['multiple protagonist', '2242'], ['necrophilia', '1828'], ['no sexual content', '324'], ['ntr', '258'], ['oral sex', '237'], ['paranormal', '408'], ['parody', '505'], ['platformer', '1508'], ['point & click', '1525'], ['possession', '1476'], ['pov', '1766'], ['pregnancy', '225'], ['prostitution', '374'], ['puzzle', '1471'], ['rape', '417'], ['real porn', '1707'], ['religion', '2218'], ['romance', '330'], ['rpg', '45'], ['sandbox', '2257'], ['scat', '689'], ['school setting', '547'], ['sci-fi', '141'], ['sex toys', '2216'], ['sexual harassment', '670'], ['shooter', '1079'], ['shota', '749'], ['side-scroller', '776'], ['simulator', '448'], ['sissification', '2215'], ['slave', '44'], ['sleep sex', '1305'], ['spanking', '769'], ['strategy', '628'], ['stripping', '480'], ['superpowers', '354'], ['swinging', '2234'], ['teasing', '351'], ['tentacles', '215'], ['text based', '522'], ['titfuck', '411'], ['trainer', '199'], ['transformation', '875'], ['trap', '362'], ['turn based combat', '452'], ['twins', '327'], ['urination', '1254'], ['vaginal sex', '2209'], ['virgin', '833'], ['virtual reality', '895'], ['voiced', '1506'], ['vore', '757'], ['voyeurism', '485']]);
function likeFilter() {
const element = document.querySelectorAll('.resource-tile');
document.querySelectorAll('.customTags').forEach(e => e.parentNode.remove());
for (let i = 0; i < element.length; i += 1) {
element[i].style = '';
element[i].classList.remove('border-gradient');
element[i].setAttribute('isDisplay', 'false');
const colors = [];
const node = document.createElement('DIV');
const dataTags = element[i].dataset.tags.split(',');
node.style.padding = '.5rem 0';
for (let j = likes.length - 1; j >= 0; j -= 1) {
if (dataTags.includes(likes[j][2])) {
console.debug(likes[j][0], likes[j][1]);
if (!element[i].classList.contains('border-gradient')) {
element[i].classList.add('border-gradient');
}
const node1 = document.createElement('SPAN');
colors.push(likes[j][1]);
node1.classList.add('customTags');
node1.style.borderLeft = `${likes[j][1]} solid`;
node1.appendChild(document.createTextNode(likes[j][0]));
node.appendChild(node1);
element[i].setAttribute('isDisplay', 'true');
} else if (element[i].getAttribute('isDisplay') === null) {
element[i].setAttribute('isDisplay', 'false');
}
}
if (colors.length > 1) {
element[i].style.borderImageSource = `linear-gradient(to bottom right, ${colors.toString()})`;
} else {
element[i].style.color = colors[0];
}
if (element[i].getAttribute('isDisplay') === 'true') {
try {
element[i].childNodes[0].childNodes[1].childNodes[1].appendChild(node);
} catch (e) {
console.error(e);
setTimeout(likeFilter, 200);
}
}
}
}
function likeLimit() {
const element = document.querySelectorAll('.resource-tile_info'); // if (likesLimit === '') {
// likesLimit = -1;
// }
for (let i = 0; i < element.length; i++) {
const elementLikes = element[i].childNodes[1].childNodes[1].innerText;
element[i].childNodes[0].childNodes[0].childNodes[0].innerText;
const checkFilter = Number(elementLikes) <= Number(GM_getValue('like_limit', 200)); // console.debug(`we get Like limit of ${elementName} is ${elementLikes} with check ${checkFilter} with display attribute of ${(element[i].parentNode.parentNode.parentNode.getAttribute('isDisplay').toLowerCase() !== 'false')}`);
if (checkFilter) {
if (element[i].parentNode.parentNode.parentNode.getAttribute('isDisplay') === 'false') {
element[i].parentNode.parentNode.parentNode.setAttribute('isLikeLimit', 'true');
continue;
}
if (GM_getValue('favLikeFilter', false)) {
element[i].parentNode.parentNode.parentNode.setAttribute('isLikeLimit', 'true');
continue;
}
}
element[i].parentNode.parentNode.parentNode.setAttribute('isLikeLimit', 'false');
}
}
function filterOut() {
const element = document.querySelectorAll('.resource-tile');
for (let i = 0; i < element.length; i++) {
if (element[i].getAttribute('isDislike') === 'true' || element[i].getAttribute('isLikeLimit') === 'true') {
element[i].style.setProperty('display', 'none', 'important');
element[i].style.setProperty('height', '0px', 'important');
element[i].style.setProperty('margin', '0px', 'important');
} else {
element[i].style.setProperty('display', 'block', 'important');
element[i].style.setProperty('height', 'unset', 'important');
element[i].style.setProperty('margin', 'unset', 'important');
}
}
}
function checkTags(element, dataTags) {
let tagProt = [['female domination', '2252'], ['male protagonist', '173'], ['futa/trans protagonist', '2255']];
const tagProtNames = dislikes.filter(e => e[0].includes('protagonist'));
console.log(tagProtNames, 'tagProtNames');
const b = [];
let c = 0;
dataTags.forEach(e => {
if (tagProtNames.length >= 1) {
for (let i = 0; i < tagProtNames.length; i += 1) {
if (tagProtNames[i][1].includes(e)) {
return b.push(e);
}
}
}
if (dislikes.includes(e)) {
c += 1;
}
}); // console.debug(`b = ${b}`, c, element);
if (c > 0) {
return true;
}
if (b.length > 0) {
b.forEach(e => {
tagProt = tagProt.filter(r => r !== e);
});
for (let i = 0; i <= tagProt.length - 1; i += 1) {
console.debug(`datatags includes? = ${dataTags.includes(tagProt[i][1])} for ${tagProt[i][0]} `);
if (dataTags.includes(tagProt[i])) {
return false;
}
if (i === tagProt.length - 1) {
return true;
}
}
}
console.debug(`tagProt = ${tagProt}`);
return false;
}
function dislikeFilter() {
const element = document.querySelectorAll('.resource-tile');
for (let i = 0; i < element.length; i++) {
const dataTags = element[i].dataset.tags.split(',');
const isFP = checkTags(element[i], dataTags);
if (isFP) {
element[i].setAttribute('isDislike', 'true');
} else {
element[i].setAttribute('isDislike', 'false');
}
}
}
function tagRemoveHandler(event, isLikes) {
let filtered = [];
if (isLikes) {
filtered = likes.filter(r => r[0] !== event.target.textContent);
console.debug(`You have clicked the Like Tag to Remove it and the filtered is: ${filtered} within ${likes}`);
const removed = _.remove(likes, n => {
return n[0] === event.target.textContent;
});
console.debug(`after filtred you got likes tags: ${likes} and you removed ${removed}`);
likeFilter();
likeLimit();
} else {
filtered = dislikes.filter(r => r[0] !== event.target.textContent);
console.debug(`You have clicked the dislike Tag to Remove it and the filtered is: ${filtered} within ${dislikes}`);
const removed = _.remove(dislikes, n => {
return n[0] === event.target.textContent;
});
console.debug(`after filtred you got dislike tags: ${dislikes} and you removed ${removed}`);
dislikeFilter();
}
event.target.remove();
GM_setValue(isLikes ? 'likes' : 'dislikes', filtered);
filterOut();
} // function tagRemoveHandler1(event) {
// likes = likes.filter(r => r[0] !== event.target.textContent);
// event.target.remove();
// GM_setValue('likes', likes);
// likeFilter();
// likeLimit();
// filterOut();
// }
function likeHandler(event) {
const [tagName, tagCode] = event.target.value.split(':');
event.target.value = '';
let x;
do {
x = prompt("Please Enter the Color in Hex like '#FFFFFF'");
} while (!x.includes('#') && x === '' && x.length <= 1);
console.debug(`you have entered color in hex: ${x} with tagName: ${tagName} and the condition ${x !== null}`);
if (x !== null) {
const tag = [tagName, x, tagCode];
likes.push(tag);
console.debug(`the likes after pushing the ${tag} is ${likes}`);
GM_setValue('likes', _.sortBy(likes, item => item[0]));
const node = document.createElement('SPAN');
node.onclick = e => tagRemoveHandler(e, true);
node.style.borderLeft = `${x} solid`;
node.appendChild(document.createTextNode(tagName));
event.target.parentNode.parentNode.childNodes[2].append(node);
likeFilter();
likeLimit();
filterOut();
}
}
function dislikeHandler(event) {
const [tagName, tagCode] = event.target.value.split(':');
dislikes.push([tagName, tagCode]);
GM_setValue('dislikes', _.sortBy(dislikes));
event.target.value = '';
const node = document.createElement('SPAN');
node.onclick = e => tagRemoveHandler(e, false);
node.appendChild(document.createTextNode(tagName));
event.target.parentNode.parentNode.childNodes[2].append(node);
dislikeFilter();
filterOut();
}
function limitHandler(event) {
GM_setValue('like_limit', event.target.value);
likeLimit();
filterOut();
}
function clickTotalTags() {
const y = document.querySelector('.selectize-control');
if (y !== null) {
y.addEventListener('click', e => {
const x = document.querySelectorAll('div.option');
console.log('clicked', x);
if (x !== []) {
const totalTags = [];
x.forEach(e => totalTags.push([e.childNodes[0].innerHTML, e.dataset.value]));
GM_setValue('tags', totalTags);
}
});
}
}
function init() {
const element = document.querySelectorAll('.resource-tile');
const elementLength = element.length;
if (element[0]) {
likeFilter();
clickTotalTags();
for (let i = 0; i < 3; i++) {
dislikeFilter();
likeLimit();
filterOut(); // if(document.querySelectorAll('.resource-tile'))
const filtered = _.filter(document.querySelectorAll('.resource-tile'), e => {
return e.getAttribute('isDisplay') === 'true';
});
console.debug('Filtered', filtered);
if (filtered.length < elementLength) {
break;
}
}
} else {
setTimeout(init, 100);
}
}
function waitForElementToDisplayAndAppend(selector, callback, checkFrequencyInMs, timeoutInMs) {
const startTimeInMs = Date.now();
(function loopSearch() {
if (document.querySelector(selector).childNodes.length > 0) {
document.querySelector(selector).appendChild(callback());
} else {
setTimeout(() => {
if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs) return;
loopSearch();
}, checkFrequencyInMs);
}
})();
}
function LikesTagFilter() {
// filter-block accordion-block filter-block_prefix-group
return VM.hm("div", {
className: "filter-block accordion-block filter-block_prefix-group"
}, VM.hm("h4", {
className: "filter-block_title accordion-toggle"
}, "Like Tags"), VM.hm("div", {
className: "filter-block_content filter-block_v accordion-content"
}, VM.hm("div", {
className: "selectize-input"
}, VM.hm("input", {
autoComplete: "on",
placeholder: "Enter a tag to filter...",
className: "input-tag",
onChange: likeHandler,
list: "totalTagName"
})), VM.hm("datalist", {
id: "totalTagName"
}, totalTags.map(item => {
return VM.hm("option", {
value: `${item[0]}:${item[1]}`
});
})), VM.hm("div", {
className: "selected-tags-wrap"
}, likes.map(dl => {
const node = document.createElement('SPAN');
node.onclick = e => tagRemoveHandler(e, true);
node.style.borderLeft = `${dl[1]} solid`;
node.appendChild(document.createTextNode(dl[0]));
return node; // <span
// style={{ borderLeft: `${dl[1]} solid` }}
// onClick={(e) => tagRemoveHandler(e, true)}
// >
// {dl[0]}
// </span>;
}))));
}
function DislikeTagFilter() {
return VM.hm("div", {
className: "filter-block accordion-block filter-block_prefix-group"
}, VM.hm("h4", {
className: "filter-block_title accordion-toggle"
}, "Dislike Tags"), VM.hm("div", {
className: "filter-block_content filter-block_v accordion-content"
}, VM.hm("div", {
className: "selectize-input"
}, VM.hm("input", {
autoComplete: "off",
placeholder: "Enter a tag to filter...",
className: "input-tag",
onChange: dislikeHandler,
list: "totalTagName1"
})), VM.hm("datalist", {
id: "totalTagName1"
}, totalTags.map(item => {
return VM.hm("option", {
value: `${item[0]}:${item[1]}`
});
})), VM.hm("div", {
className: "selected-tags-wrap"
}, dislikes.map(dl => VM.hm("span", {
onClick: e => tagRemoveHandler(e, false)
}, dl[0])))));
}
function LikeLimitDisplay() {
return VM.hm("div", {
className: "filter-block"
}, VM.hm("h4", {
className: "filter-block_title"
}, "Tags Like Limits"), VM.hm("div", {
className: "selectize-input"
}, VM.hm("input", {
value: GM_getValue('like_limit', 200),
autoComplete: "off",
style: "width: 127px;",
onChange: limitHandler
})));
}
function FilterFavLikeHandler(event) {
GM_setValue('favLikeFilter', event.target.checked);
likeLimit();
filterOut();
}
function mainFilter() {
init();
return VM.hm(VM.Fragment, null, LikeLimitDisplay(), VM.hm("div", {
className: "filter-block"
}, VM.hm("div", {
className: "filter-block_button-wrap",
style: {
display: 'flex'
}
}, VM.hm("span", {
className: "filter-block_title flxgrow"
}, "Filter Fav likes:"), VM.hm("input", {
type: "checkbox",
id: "filter-fav-likes",
onChange: FilterFavLikeHandler,
checked: GM_getValue('favLikeFilter', false)
}))), VM.hm("style", null, css_248z));
}
document.querySelector('.content-block_filter').appendChild(mainFilter());
waitForElementToDisplayAndAppend('#filter-block_prefixes', LikesTagFilter, 1000, 10000);
waitForElementToDisplayAndAppend('#filter-block_prefixes', DislikeTagFilter, 1000, 10000);
history.onpushstate = function (state) {
setTimeout(init, 200);
};
(function (history) {
const pushState = history.pushState;
history.pushState = function (state) {
if (typeof history.onpushstate === 'function') {
history.onpushstate({
state
});
}
return pushState.apply(history);
};
})(window.history);
})();