Hitomi - Search & UI Tweaks

Search Filters & UI Manipulations

Per 08-02-2019. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Hitomi - Search & UI Tweaks
// @namespace    brazenvoid
// @version      2.6.1
// @author       brazenvoid
// @license      GPL-3.0-only
// @description  Search Filters & UI Manipulations
// @include      https://hitomi.la/*
// @run-at       document-idle
// ==/UserScript==

// Define languages to keep and tags to exclude here

let settings = {
  allowedGalleryTypes: [
    'artist cg',
    'doujinshi',
    'game cg',
    'manga',
  ],
  allowedLanguages: [
    'japanese',
    'english'
  ],
  excludedTags: [
    'female:daughter',
    'female:mother',
    'sample',
    'male:father',
    'male:son',
    'male:yaoi'
  ],
  excludedTagGroups: [
    ['female:loli', 'female:sole female'],
    ['male:shota', 'male:sole male'],
  ],
  removeRelatedGalleries: true,
}

// Translate gallery types to css selectors

let allowedGallerySelectors = [], allowedGalleryTypeSelector

for (let allowedGalleryType of settings.allowedGalleryTypes) {
  switch (allowedGalleryType) {
    case 'anime':
      allowedGalleryTypeSelector = 'anime'
      break
    case 'artist cg':
      allowedGalleryTypeSelector = 'acg'
      break
    case 'doujinshi':
      allowedGalleryTypeSelector = 'dj'
      break
    case 'game cg':
      allowedGalleryTypeSelector = 'cg'
      break
    case 'manga':
      allowedGalleryTypeSelector = 'manga'
      break
    default:
      continue
  }
  allowedGallerySelectors.push(allowedGalleryTypeSelector)
}

// Formatting filters

let formatFilters = function (filters, prefix, suffix, join) {

  let formatFilter = function (filter) {
    return '[href="' + prefix + encodeURIComponent(filter) + suffix + '.html"]'
  }
  let index2
  for (let index = 0; index < filters.length; index++) {
    if (Array.isArray(filters[index])) {
      for (index2 = 0; index2 < filters[index].length; index2++) {
        filters[index][index2] = formatFilter(filters[index][index2])
      }
    } else {
      filters[index] = formatFilter(filters[index])
    }
  }
  return join ? filters.join(', ') : filters
}

let languageFiltersSelector = formatFilters(settings.allowedLanguages, '/index-', '-1', true)
let tagFiltersSelector = formatFilters(settings.excludedTags, '/tag/', '-all-1', true)
let tagGroupFiltersSelectors = formatFilters(settings.excludedTagGroups, '/tag/', '-all-1', false)

// Filtration logic

let validateLanguage = function (gallery) {

  let validationCheck = true

  if (settings.allowedLanguages.length > 0) {

    let languageTD = gallery.querySelector('tr:nth-child(3) > td:nth-child(2)')
    if (languageTD.querySelector('a') !== null) {
      validationCheck = languageTD.querySelectorAll(languageFiltersSelector).length > 0
    }
  }
  return validationCheck
}

let validateTags = function (gallery) {

  let validationCheck = true

  if (settings.excludedTags.length > 0) {
    validationCheck = gallery.querySelectorAll(tagFiltersSelector).length === 0
  }
  if (validationCheck && settings.excludedTagGroups.length > 0) {
    for (let tagGroupFilterSelectors of tagGroupFiltersSelectors) {
      validationCheck = gallery.querySelectorAll(tagGroupFilterSelectors.join(', ')).length < tagGroupFilterSelectors.length
      if (!validationCheck) {
        break
      }
    }
  }
  return validationCheck
}

let validateType = function (gallery) {
  return allowedGallerySelectors.includes(gallery.className)
}

let complianceCallback = function (target) {

  let galleries = target.querySelectorAll('.anime, .manga, .dj, .acg, .cg')
  let validationCheck

  for (let gallery of galleries) {

    validationCheck = validateType(gallery) && validateLanguage(gallery) && validateTags(gallery)

    if (!validationCheck) {
      gallery.remove()
    }
  }
}

// Script Run

let galleriesList = document.querySelector('.gallery-content')
let isGalleryPage = document.getElementById('dl-button') !== null

if (isGalleryPage && settings.removeRelatedGalleries) {

  galleriesList.remove()

} else {

  let observerConfig = {
    attributes: false,
    childList: true,
    subtree: false
  }
  complianceCallback(galleriesList)

  // Adding observer to check compliance of galleries

  let observer = new MutationObserver(function (mutations) {
    for (let mutation of mutations) {
      complianceCallback(mutation.target)
    }
  })
  observer.observe(galleriesList, observerConfig)
}