// ==UserScript==
// @name E-Hentai - Color Results By Tags
// @description Highlights galleries with tag flags using the color(s) of their own tag flags.
// @match https://e-hentai.org/*
// @match https://exhentai.org/*
// @run-at document-end
// @version 0.0.3
// @namespace https://greasyfork.org/users/2168
// ==/UserScript==
function process() {
extractTargets().forEach(function(target,n) {
if (target.colors.length > 0) {
target.item.style.backgroundColor = applyOpacity(chooseColor(target.colors));
target.item.classList.add('eh-highlighted');
} else {
target.item.style.backgroundColor = null;
target.item.classList.remove('eh-highlighted');
}
if (target.item.nodeName === 'TR')
target.item.parentNode.appendChild(target.item);
else
target.item.style.order = n;
});
// Reposition header row if necessary (only needed for compact mode)
var header = document.querySelector('.eh-highlighted + tr > th');
if (header) header.parentNode.parentNode.insertBefore(header.parentNode, header.parentNode.parentNode.firstChild);
}
function extractTargets() {
var targets = qSA('.itg > tbody > tr, .itg > .gl1t');
var highlighted = [ ], ignored = [ ];
targets.forEach(function(target) {
var tags = qSA('.gt[style*="color"]', target);
if (tags.length === 0) ignored.push({ item: target, colors: [ ] });
else {
var colors = [ ];
tags.forEach(function(tag) { colors.push(extractColor(tag)); });
highlighted.push({ item: target, colors: colors });
}
});
return highlighted.concat(ignored);
}
/*------------------
Color Manipulation
------------------*/
// Extracts the background color of a given tag (always picks the lighter color in the gradient)
function extractColor(element) {
try {
var background = element.style.background.replace(/\s/g, '');
var colors = background.match(/rgb\(\d+,\d+,\d+\)/g)
.concat(background.match(/rgba\(\d+,\d+,\d+,\d+\)/g))
.concat(background.match(/#[0-9a-f]{2,8}/));
if (colors.length < 2) return parseColor(element.style.borderColor);
var parsed = [ parseColor(colors[0]), parseColor(colors[1]) ];
var distance1 = distance(parsed[0], [ 255, 255, 255 ]);
var distance2 = distance(parsed[1], [ 255, 255, 255 ]);
return (distance1 < distance2 ? parsed[0] : parsed[1]);
} catch (e) {
return parseColor(element.style.borderColor);
}
}
// Chooses which color to assign to the item/row (based on color weights)
function chooseColor(colors) {
var map = { };
var max = 0;
colors.forEach(function(color) {
var key = color.join(',');
if (!map.hasOwnProperty(key)) map[key] = [ color, 1 ];
else ++map[key][1];
max = Math.max(max, map[key][1]);
});
var result = Object.keys(map)
.filter(function(key) { return map[key][1] === max; })[0];
return map[result][0];
}
// Parses a color into a numeric list of 3 elements from 0 to 255
function parseColor(color) {
try {
color = color.replace(/\s/g, '').trim();
var tokens = color.match(/^rgba?\((\d+),(\d+),(\d+)(?:,[\d.]+)?\)/i);
if (tokens) return [ parseInt(tokens[1], 10), parseInt(tokens[2], 10), parseInt(tokens[3], 10) ];
if (/^#[0-9a-f]{3,3}$/.test(color)) color = color + color.slice(1);
var hex = color.match(/^#([0-9a-f]{2,2})([0-9a-f]{2,2})([0-9a-f]{2,2})/i);
if (hex) return [ parseInt(tokens[1], 16), parseInt(tokens[2], 16), parseInt(tokens[3], 16) || 255 ];
} catch (e) {
return [ 255, 255, 255 ];
}
}
// Calculates the Euclidean distance between two colors
function distance(from, to) {
return Math.sqrt(Math.pow(from[0] - to[0], 2) + Math.pow(from[1] - to[1], 2) + Math.pow(from[2] - to[2], 2));
}
// Applies opacity to a given background color
function applyOpacity(color) {
var opacity = 0.6;
var result = color.map(function(c) { return Math.round(c + (255 - c) * (1 - opacity)); });
return 'rgb(' + result.join(',') + ')';
}
/*---------
Utilities
---------*/
function qSA(query, parent) {
if (!parent) parent = document;
return [].slice.call(parent.querySelectorAll(query));
}
/*--------------
Initialization
--------------*/
var style = document.createElement('style');
style.innerHTML = '.eh-highlighted * { color: black !important; }' +
'.eh-highlighted .glname:hover > a { color: #333333 !important; }';
document.head.appendChild(style);
process();