Nc4lsD09 / wikipedia_similar_pages

// ==UserScript==
// @name        wikipedia_similar_pages
// @namespace   https://catherine.v0cyc1pp.com/
// @match       https://*.wikipedia.org/wiki/*
// @author      greg10
// @run-at      document-end
// @license     GPL-3.0-or-later
// @version     0.2
// @grant       none
// @description 今見ているページの類似ページをページ下部に付ける
// ==/UserScript==

console.log("wikipedia_similar_pages");

function get_title_langcode() {
    var langcode = "ja";
    var title = "";

    var matches = decodeURI(location.href).match(
        "https?:\/\/([a-z]+)\.wikipedia\.org\/wiki\/([^#]*)",
    );
    if (matches) {
        langcode = matches[1];
        title = matches[2];
    }

    return { title: title, langcode: langcode };
}

function create_container() {
    //--------------------------------------
    // 一番外枠:misc_container
    //--------------------------------------
    var misc_container = document.createElement("div");

    misc_container.id = "ruiji_container";
    misc_container.className = "ruiji_container";
    //misc_container.style.display = "flex";
    //misc_container.style.flexFlow = "column";
    //misc_container.style.position = 'fixed';
    //misc_container.style.top = "85px";
    //misc_container.style.left = 0;
    //misc_container.style.right = "10px";
    misc_container.style.borderStyle = "solid";
    misc_container.style.borderColor = "green";
    misc_container.style.borderWidth = "thin";
    misc_container.style.padding = "4px";

    // 追加
    misc_container.innerHTML = "<h3>さらに読む</h3><p>処理中です...</p>";

    var main_elem = document.querySelector(".mw-content-container");

    main_elem.appendChild(misc_container);

    return misc_container;
}

//JSONデータを引数に受け取ってDOM操作を行う関数を作成
function proc_json(misc_container, json, langcode) {
    misc_container.innerHTML = "<h3>さらに読む</h3>\n";

    const wrapper = document.createElement("div");
    wrapper.className = "ruiji_cards";
    misc_container.appendChild(wrapper);

    for (var index in json.pages) {
        var page = json.pages[index];

        var title = page.title;
        var url = "https://" + langcode + ".wikipedia.org/wiki/" + title;
        var desc = page.description;
        var thumburl = page.thumbnail ? page.thumbnail.url : null;

        const card = document.createElement("a");
        card.className = "ruiji_card";
        card.href = url;
        //card.target = "_blank"; // 別タブで開きたい場合

        // サムネ
        if (thumburl) {
            const img = document.createElement("img");
            img.src = thumburl;
            img.className = "ruiji_thumb";
            card.appendChild(img);
        }

        // 本文
        const body = document.createElement("div");
        body.className = "ruiji_body";

        const titleEl = document.createElement("div");
        titleEl.textContent = title;
        titleEl.className = "ruiji_title";

        body.appendChild(titleEl);

        if (desc) {
            const d = document.createElement("div");
            d.textContent = desc;
            d.className = "ruiji_desc";
            body.appendChild(d);
        }

        card.appendChild(body);
        wrapper.appendChild(card);
    }
}

function proc(misc_container, title, langcode) {
    var api =
        "https://" +
        langcode +
        ".wikipedia.org/w/rest.php/v1/search/page?q=morelike:" +
        encodeURI(title) +
        "&limit=10";

    console.log("api=" + api);
    //APIからJSONデータを取得する
    fetch(api, {
        method: "GET",
        headers: {
            "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
        },
    })
        .then((response) => {
            return response.json(); //ここでBodyからJSONを返す
        })
        .then((result) => {
            proc_json(misc_container, result, langcode); //取得したJSONデータを関数に渡す
        })
        .catch((e) => {
            console.log(e); //エラーをキャッチし表示
        });
}

function add_style() {
    const style = document.createElement("style");
    style.textContent = `
    .ruiji_cards {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
        gap: 10px;
    }

    .ruiji_card {
        display: flex;
        gap: 10px;
        cursor: pointer;
        border: 1px solid #ccc;
        border-radius: 8px;
        padding: 8px;
        background: #fafafa;
        transition: 0.2s;

        text-decoration: none;   /* ←追加 */
        color: inherit;          /* ←追加 */
    }

    .ruiji_card:hover {
        background: #f0f0f0;
        transform: translateY(-2px); /* ←ちょい浮かせると気持ちいい */
    }

    .ruiji_card:active {
        transform: scale(0.98);
    }

    .ruiji_thumb {
        width: 80px;
        height: 80px;
        object-fit: cover;
        border-radius: 4px;
        flex-shrink: 0;
    }

    .ruiji_body {
        display: flex;
        flex-direction: column;
        font-size: 14px;
    }

    .ruiji_title {
        font-weight: bold;
        text-decoration: none;
        margin-bottom: 4px;
    }

    .ruiji_desc {
        color: #555;
        font-size: 12px;
    }

    @media (prefers-color-scheme: dark) {
        .ruiji_card {
            background: #1e1e1e;
            border: 1px solid #444;
            color: #ddd;
        }

        .ruiji_card:hover {
            background: #2a2a2a;
        }

        .ruiji_title {
            color: #fff;
        }

        .ruiji_desc {
            color: #aaa;
        }
    }

    `;
    document.head.appendChild(style);
}
function main() {
    add_style(); // ←追加

    var { title, langcode } = get_title_langcode();

    /*
    // 標準空間だけ対象にする
    var ns = mw.config.get('wgNamespaceNumber')
    if ( ns != 0 ) return;
    */

    /*
    // Content Script内で実行可能
    const nsIdMeta = document.querySelector('meta[name="wgNamespaceNumber"]');
    if (nsIdMeta) {
        const nsId = parseInt(nsIdMeta.getAttribute('content'), 10);
        console.log('Namespace ID:', nsId);
        if ( nsId != 0 ) return;
    }
    */
    var ns = RLCONF["wgNamespaceNumber"];
    console.log("ns=" + ns);
    if (ns != 0) {
        return;
    }

    var misc_container = create_container();

    proc(misc_container, title, langcode);
}

main();