eh详情页标签颜色

eh为详情页标签增加颜色

// ==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();