// ==UserScript==
// @name Hitomi - Search & UI Tweaks
// @namespace brazenvoid
// @version 3.0.0
// @author brazenvoid
// @license GPL-3.0-only
// @description Various search filters and user experience enhancers
// @match https://hitomi.la/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
// @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=884246
// @require https://greasyfork.org/scripts/418665-brazen-configuration-manager/code/Brazen%20Configuration%20Manager.js?version=880818
// @require https://greasyfork.org/scripts/416104-brazen-ui-generator/code/Brazen%20UI%20Generator.js?version=880816
// @require https://greasyfork.org/scripts/416105-brazen-base-search-enhancer/code/Brazen%20Base%20Search%20Enhancer.js?version=884245
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
GM_addStyle(
`#settings-wrapper{font-family:Open Sans;font-size:12px;font-weight:700;line-height:1;background-color:rgb(216, 210, 234);top:5vh;width:220px}button{font-family:Open Sans;font-size:12px;font-weight:700}.bg-brand{background-color:rgb(216, 210, 234)}`)
const PAGE_PATH_NAME = window.location.pathname
const IS_GALLERY_PAGE = $('#dl-button').length
const ITEM_CLASSES = ['acg', 'anime', 'cg', 'dj', 'manga']
const ITEM_CLASSES_SELECTOR = '.acg,.anime,.cg,.dj,.manga'
const FILTER_GALLERY_TYPES = 'Show Gallery Types'
const FILTER_TAG_BLACKLIST = 'Tag Blacklist'
const FILTER_COMPLEX_TAG_BLACKLIST = 'Complex Blacklist'
const FILTER_LANGUAGES = 'Languages'
const OPTION_REMOVE_RELATED_GALLERIES = 'Remove Related Galleries'
class HitomiSearchAndUITweaks extends BrazenBaseSearchEnhancer
{
constructor ()
{
super('hitomi-sui-', ITEM_CLASSES)
this._configurationManager.
addCheckboxesGroup(FILTER_GALLERY_TYPES, [
['Anime', 'anime'],
['Artist CG', 'acg'],
['Doujinshi', 'dj'],
['Game CG', 'cg'],
['Manga', 'manga'],
], 'Show only selected gallery types.').
addCheckboxesGroup(FILTER_LANGUAGES, [
['N/A', 'not-applicable'],
['Japanese', 'japanese'],
['Chinese', 'chinese'],
['English', 'english'],
['Albanian', 'albanian'],
['Arabic', 'arabic'],
['Bulgarian', 'bulgarian'],
['Catalan', 'catalan'],
['Cebuano', 'cebuano'],
['Czech', 'czech'],
['Danish', 'danish'],
['Dutch', 'dutch'],
['Esperanto', 'esperanto'],
['Estonian', 'estonian'],
['Finnish', 'finnish'],
['French', 'french'],
['German', 'german'],
['Greek', 'greek'],
['Hebrew', 'hebrew'],
['Hungarian', 'hungarian'],
['Indonesian', 'indonesian'],
['Italian', 'italian'],
['Korean', 'korean'],
['Latin', 'latin'],
['Mongolian', 'mongolian'],
['Norwegian', 'norwegian'],
['Persian', 'persian'],
['Polish', 'polish'],
['Portuguese', 'portuguese'],
['Romanian', 'romanian'],
['Russian', 'russian'],
['Slovak', 'slovak'],
['Spanish', 'spanish'],
['Swedish', 'swedish'],
['Tagalog', 'tagalog'],
['Thai', 'thai'],
['Turkish', 'turkish'],
['Ukrainian', 'ukrainian'],
['Unspecified', 'unspecified'],
['Vietnamese', 'vietnamese'],
], 'Select languages to show').
addFlagField(OPTION_REMOVE_RELATED_GALLERIES, 'Remove related galleries section from gallery pages.').
addRulesetField(
FILTER_TAG_BLACKLIST,
'Specify the tags blacklist with one tag on each line.',
(values) => {
for (let i = 0; i < values.length; i++) {
values[i] = decodeURIComponent(values[i])
}
return values
},
(values) => {
for (let i = 0; i < values.length; i++) {
values[i] = encodeURIComponent(values[i])
}
return values
},
).
addRulesetField(FILTER_COMPLEX_TAG_BLACKLIST, 'Set complex tag blacklist rules on each line.')
this._setupUI()
this._setupCompliance()
this._setupComplianceFilters()
}
/**
* @param {string} str
* @return {string}
* @private
*/
_encodeURIComponentRFC3986 (str)
{
return encodeURIComponent(str).replace(/[-!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
}
_formatFilter (prefix, filter, suffix)
{
return '[href="' + prefix + this._encodeURIComponentRFC3986(filter) + suffix + '.html"]'
}
/**
* @param filters
* @param prefix
* @param suffix
* @param join
* @return {*}
* @private
*/
_formatFilters (filters, prefix, suffix, join)
{
let formattedFilters = [], index2
for (let index = 0; index < filters.length; index++) {
if (Array.isArray(filters[index])) {
for (index2 = 0; index2 < filters[index].length; index2++) {
formattedFilters[index][index2] = this._formatFilter(prefix, filters[index][index2], suffix)
}
} else {
formattedFilters[index] = this._formatFilter(prefix, filters[index], suffix)
}
}
return join ? formattedFilters.join(', ') : formattedFilters
}
/**
* @private
*/
_removeRelatedGalleries ()
{
if (IS_GALLERY_PAGE && this._configurationManager.getValue(OPTION_REMOVE_RELATED_GALLERIES)) {
$('.gallery-content').remove()
}
}
_setupCompliance ()
{
this._onGetItemLists = () => $('.gallery-content')
this._onGetItemName = (item) => item.find('h1.lillie a').text()
}
_setupComplianceFilters ()
{
this._addItemComplianceFilter(FILTER_LANGUAGES, (valueKeys) => valueKeys.length, (item, valueKeys) => {
let languageLink = item.find('tr:nth-child(3) > td:nth-child(2) a')
if (languageLink.length) {
languageLink = languageLink.attr('href')
for (let key of valueKeys) {
if (languageLink.includes(key)) {
return true
}
}
return false
}
return valueKeys.includes('not-applicable')
})
this._addItemComplianceFilter(FILTER_GALLERY_TYPES, (valueKeys) => valueKeys.length, (item, valueKeys) => {
for (let galleryClass of valueKeys) {
if (item.hasClass(galleryClass)) {
return true
}
}
return false
})
this._addItemComplianceFilter(FILTER_TAG_BLACKLIST, (blacklistedTags) => blacklistedTags.length, (item, blacklistedTags) => {
let relatedTagsWrapper = item.find('.relatedtags')
for (let blacklistedTag of blacklistedTags) {
if (relatedTagsWrapper.find('a[href*="' + blacklistedTag + '"]').length) {
return false
}
}
return true
})
this._addItemComplianceFilter(
FILTER_COMPLEX_TAG_BLACKLIST, (complexBlacklist) => complexBlacklist.length, (item, complexBlacklist) => {
for (let tagGroupFilterSelectors of complexBlacklist) {
if (!item.find(tagGroupFilterSelectors.join(', ')).length < tagGroupFilterSelectors.length) {
return false
}
}
return true
})
}
_setupUI ()
{
this._onBeforeUIBuild = () => {
if (IS_GALLERY_PAGE) {
this._removeRelatedGalleries()
}
}
this._onUIBuild = () =>
this._uiGen.createSettingsSection().append([
this._uiGen.createTabsSection(['Filters', 'Languages', 'Statistics'], [
this._uiGen.createTabPanel('Filters', true).append([
this._configurationManager.createElement(FILTER_GALLERY_TYPES),
this._uiGen.createSeparator(),
this._configurationManager.createElement(FILTER_TAG_BLACKLIST),
// this._configurationManager.createElement(FILTER_COMPLEX_TAG_BLACKLIST),
this._uiGen.createSeparator(),
this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION),
this._uiGen.createSeparator(),
this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE),
]),
this._uiGen.createTabPanel('Languages').append([
this._configurationManager.createElement(FILTER_LANGUAGES),
]),
this._uiGen.createTabPanel('Statistics').append([
this._uiGen.createStatisticsFormGroup(FILTER_GALLERY_TYPES),
this._uiGen.createStatisticsFormGroup(FILTER_LANGUAGES),
this._uiGen.createStatisticsFormGroup(FILTER_TAG_BLACKLIST),
// this._uiGen.createStatisticsFormGroup(FILTER_COMPLEX_TAG_BLACKLIST),
this._uiGen.createSeparator(),
this._uiGen.createStatisticsTotalsGroup(),
]),
]),
this._createSettingsFormActions(),
this._uiGen.createSeparator(),
this._uiGen.createStatusSection(),
])
this._onAfterUIBuild = () => {
this._uiGen.getSelectedSection()[0].userScript = this
}
}
}
(new HitomiSearchAndUITweaks).init()