// ==UserScript==
// @name eh详情页标签颜色
// @namespace com.xioxin.tag-color
// @version 0.7
// @description eh为详情页标签增加颜色
// @author xioxin
// @homepage https://github.com/EhTagTranslation/UserScripts
// @supportURL https://github.com/EhTagTranslation/UserScripts/issues
// @match *://exhentai.org/g/*
// @match *://e-hentai.org/g/*
// @match *://exhentai.org/mytags
// @match *://e-hentai.org/mytags
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
if(typeof GM_getValue == 'undefined') {
function GM_getValue(key, val) {return JSON.parse(localStorage.getItem(key)||'null') || val }
}
if(typeof GM_addStyle == 'undefined') {
function GM_addStyle(script){
var style = document.createElement("style");
style.type = "text/css";
style.innerHTML=script
document.getElementsByTagName("HEAD").item(0).appendChild(style);
}
}
if(typeof GM_setValue == 'undefined') {
function GM_setValue(key, val) {localStorage.setItem(key, JSON.stringify(val))}
}
async function saveMyTagData() {
const msg = document.createElement('div');
msg.style.clear = 'both';
msg.style.textAlign = 'center';
msg.style.width = '100%';
document.querySelector('#tagset_outer').appendChild(msg);
const setMsg = (text) => msg.innerText = text;
try {
const tags = [];
for(let id of [...document.querySelectorAll("#tagset_outer select option")].map(v=> v.value)) {
setMsg(`[详情页标签颜色]正在加载 ${id}`);
tags.push(...await loadMyTagData(id));
}
// 从小到大排序,因为颜色渲染是css,靠后的权重更大.
tags.sort((a,b) => a.weight - b.weight);
GM_setValue("myTags", tags);
setMsg(`[详情页标签颜色] 已更新 共${tags.length}个`);
} catch (e) {
setMsg(`[详情页标签颜色] 错误: ${e.message}`);
}
}
async function loadMyTagData(tageset) {
const html = await fetch(`${document.location.origin}/mytags?tagset=${tageset || 1}`, {credentials: "include"}).then(v => v.text());
const safeHtml = html.replace(/^.*<body>(.*)<\/body>.*$/igms,"$1").replace(/<script.*?>(.*?)<\/script>/igms, '');
const dom = document.createElement('div');
dom.innerHTML = safeHtml;
const tags = [...dom.querySelectorAll('#usertags_outer>div')].map(e => {
if(e.querySelector('.gt') == null) return
return {
tag: e.querySelector('.gt').title,
background: e.querySelector('.gt').style.background,
color: e.querySelector('.gt').style.color,
borderColor: e.querySelector('.gt').style.borderColor,
weight: parseInt(e.querySelector('[id^=tagweight]').value, 10),
}
}).filter(v => v);
return tags;
}
async function dyeing() {
setTimeout(colorIcon, 0);
const myTags = GM_getValue("myTags", []);
let css = '';
myTags.forEach(v => {
const key = v.tag.replaceAll(' ', '_');
css += `
[id="td_${key}"]{
border-color: ${v.borderColor} !important;
background: ${v.background} !important;
}
[id="td_${key}"].gtw, [id="td_${key}"].gtl{
outline: solid 1px ${v.borderColor};
border-color: ${v.color} !important;
}
[id="td_${key}"] a {
color: ${v.color};
}
.tup::after, .tdn::after {
display: inline-block;
color: #fff;
border-radius: 4px;
margin-left: 4px;
margin-right: -2px;
background-color: green;
content: '';
line-height: 14px;
outline: solid 1px #fff;
vertical-align: sub;
width: 4px;
height: 14px;
}
.tdn::after {
background-color: red;
}
`
});
GM_addStyle(css);
}
function colorIcon() {
const myTags = GM_getValue("myTags", []);
const tagIds = [...document.querySelectorAll("#taglist td>div")].map(v => v.id.replace('td_', '').replaceAll('_', ' '));
const tags = tagIds.map(id => myTags.find(v => v.tag == id)).filter(v => v);
tags.sort((a, b) => parseInt(a.weight) - parseInt(b.weight));
const weight = tags.reduce((accumulator, tag) => accumulator + parseInt(tag.weight), 0);
const colors = tags.map(tag => (/(rgb\(.*?\))/ig.exec(tag.borderColor)||[])[0]).filter(v => v);
if(!colors.length) return;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = 128;
const edgeSize = 8;
let ctx = canvas.getContext("2d");
colors.forEach((c, i) => {
ctx.fillStyle = ctx.strokeStyle = c;
ctx.fillRect((canvas.width - edgeSize*2) / colors.length * i + edgeSize, 0, (canvas.width - edgeSize*2) / colors.length, canvas.height);
});
ctx.globalCompositeOperation="destination-in";
ctx.fillStyle = "rgb(0,0,0)";
ctx.roundRect(edgeSize/2,edgeSize/2,canvas.width - edgeSize,canvas.height - edgeSize, 20).fill();
ctx.globalCompositeOperation="source-over";
ctx.lineWidth = edgeSize;
ctx.strokeStyle = "#5C0D11";
ctx.stroke();
ctx.font = '100px Consolas, Monaco, monospace';
const tw = ctx.measureText("w").width;
const fs = Math.min((( 100 / (tw * 3))) * canvas.width, canvas.width );
ctx.font = `${fs.toFixed(2)}px Consolas, Monaco, monospace`;
ctx.fillStyle = "#5C0D11";
ctx.strokeStyle = "#FFF";
const t = `${weight}`;
const tl = t.length > 2 ? edgeSize : edgeSize * 2;
ctx.strokeText(`${weight}`,tl, fs);
ctx.fillText(`${weight}`, tl, fs);
canvas.toBlob(function(blob) {
const link = canvas.toDataURL('image/png');
const favicon = document.createElement("link");
favicon.rel = "icon";
favicon.href = URL.createObjectURL(blob);
document.head.appendChild(favicon);
});
}
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.beginPath();
this.moveTo(x+r, y);
this.arcTo(x+w, y, x+w, y+h, r);
this.arcTo(x+w, y+h, x, y+h, r);
this.arcTo(x, y+h, x, y, r);
this.arcTo(x, y, x+w, y, r);
this.closePath();
return this;
}
if(window.location.pathname == "/mytags")saveMyTagData();
if(window.location.pathname.slice(0, 3) == '/g/')dyeing();