f95zone tweaks

f95zone exclude tags and min like filter.

目前為 2021-08-25 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        f95zone tweaks
// @namespace   f95zone tweaks
// @description f95zone exclude tags and min like filter.
// @match       https://f95zone.to/latest/
// @version     1.3.5
// @author      3xd_Tango
// @require     https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@1,npm/@violentmonkey/[email protected]
// @require     https://cdn.jsdelivr.net/npm/lodash@4/lodash.min.js
// @grant       GM_addStyle
// @grant       GM_getValue
// @grant       GM_setValue
// ==/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{width:127px;opacity:1;position:relative;left:0}.border-gradient{border:3px solid;border-image-slice:1}";

const likes = GM_getValue('likes', [['incest', '#ff0000'], ['harem', '#0011ff'], ['futa/trans', '#EE82EE'], ['loli', '#00f5ffff'], ['futa/trans protagonist', '#EE82EE']]);
const dislikes = GM_getValue('dislikes', ['female protagonist', 'text based', 'mind control']);
const totalTags = GM_getValue('tags', []);

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].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][0])) {
        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.backgroundColor = likes[j][1];
        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;
    const elementName = 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').toLowerCase() === 'false') {
        element[i].parentNode.parentNode.parentNode.setAttribute('isLikeLimit', 'true');
      } else {
        element[i].parentNode.parentNode.parentNode.setAttribute('isLikeLimit', 'false');
      }
    } else {
      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 protagonist', 'male protagonist', 'futa/trans protagonist'];
  const tagProtNames = dislikes.filter(e => e.includes('protagonist'));
  const b = [];
  let c = 0;
  dataTags.forEach(e => {
    if (tagProtNames.length >= 1) {
      if (tagProtNames.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++) {
      console.debug(`datatags includes? = ${dataTags.includes(tagProt[i])} for ${tagProt[i]} `);

      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 !== 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 === 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 = event.target.value;
  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];
    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.appendChild(document.createTextNode(tagName));
    event.target.parentNode.parentNode.childNodes[2].append(node);
    likeFilter();
    likeLimit();
    filterOut();
  }
}

function dislikeHandler(event) {
  const tagName = event.target.value;
  dislikes.push(tagName);
  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 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.innerHTML));
        GM_setValue('tags', totalTags);
      }
    });
  }
}

function limitHandler(event) {
  GM_setValue('like_limit', event.target.value);
  likeLimit();
  filterOut();
}

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');
      });

      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.createElement("div", {
    className: "filter-block accordion-block filter-block_prefix-group"
  }, VM.createElement("h4", {
    className: "filter-block_title accordion-toggle"
  }, "Like Tags"), VM.createElement("div", {
    className: "filter-block_content filter-block_v accordion-content"
  }, VM.createElement("div", {
    className: "selectize-input"
  }, VM.createElement("input", {
    autoComplete: "on",
    placeholder: "Enter a tag to filter...",
    className: "input-tag",
    onChange: likeHandler,
    list: "totalTagName"
  })), VM.createElement("datalist", {
    id: "totalTagName"
  }, totalTags.map(item => {
    return VM.createElement("option", {
      value: item
    });
  })), VM.createElement("div", {
    className: "selected-tags-wrap"
  }, likes.map(dl => VM.createElement("span", {
    onClick: e => tagRemoveHandler(e, true),
    style: {
      backgroundColor: dl[1]
    }
  }, dl[0])))));
}

function DislikeTagFilter() {
  return VM.createElement("div", {
    className: "filter-block accordion-block filter-block_prefix-group"
  }, VM.createElement("h4", {
    className: "filter-block_title accordion-toggle"
  }, "Dislike Tags"), VM.createElement("div", {
    className: "filter-block_content filter-block_v accordion-content"
  }, VM.createElement("div", {
    className: "selectize-input"
  }, VM.createElement("input", {
    autoComplete: "off",
    placeholder: "Enter a tag to filter...",
    className: "input-tag",
    onChange: dislikeHandler,
    list: "totalTagName1"
  })), VM.createElement("datalist", {
    id: "totalTagName1"
  }, totalTags.map(item => {
    return VM.createElement("option", {
      value: item
    });
  })), VM.createElement("div", {
    className: "selected-tags-wrap"
  }, dislikes.map(dl => VM.createElement("span", {
    onClick: e => tagRemoveHandler(e, false)
  }, dl)))));
}

function LikeLimitDisplay() {
  return VM.createElement("div", {
    className: "filter-block"
  }, VM.createElement("h4", {
    className: "filter-block_title"
  }, "Tags Like Limits"), VM.createElement("div", {
    className: "selectize-input"
  }, VM.createElement("input", {
    value: GM_getValue('like_limit', 200),
    autoComplete: "off",
    style: "width: 127px;",
    onChange: limitHandler
  })));
}

function mainFilter() {
  init();
  return VM.createElement(VM.Fragment, null, LikeLimitDisplay(), VM.createElement("div", {
    className: "filter-block"
  }, VM.createElement("h4", {
    className: "filter-block_title"
  }, "Filter Fav likes: ", VM.createElement("input", {
    type: "checkbox",
    id: "filter-fav-likes"
  }))), VM.createElement("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);

}());