Greasy Fork

Greasy Fork is available in English.

ForumLive

Outil pour faciliter l'actualisation de la liste des topics sur les forums de Jeuxvideo.com

当前为 2020-02-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ForumLive
// @namespace    ForumLive
// @version      0.1.3
// @description  Outil pour faciliter l'actualisation de la liste des topics sur les forums de Jeuxvideo.com
// @author       Me
// @match        http://*.jeuxvideo.com/forums/0-*
// @match        https://*.jeuxvideo.com/forums/0-*
// @grant        none
// ==/UserScript==

let forumUrl = undefined;
let initialized = false;
let intervalId = undefined;
let timeoutId = undefined;
let displayedPage = 0;
let currentMode = "classic";
let currentConnected = undefined;
let nbMps = undefined;
let nbNotifs = undefined;
let currentlyDisplayedTopics = undefined;
let dataTopics = {};
let requestsCounter = 0;
let topicsDOM = [];
let topicsLength = undefined;
let isOver = false;
let pagiBeforeSaved = undefined;
let pagiAfterSaved = undefined;

function addCss() {
    let rules = `
        #forum-live-mode {
        }

        #forum-live-button {
            margin-left: -87px;
        }

        .forum-live-activated .bloc-pagi-default {
            height: 28px;
        }

        .forum-live-activated .forum-live-tempo {
            border-color: blue;
        }

        .forum-live-activated .conteneur-topic-pagi:hover .topic-list {
            border-color: red;
        }

        .forum-live-hide {
            display: none!important;
        }

        .forum-live-hide-pagi-before .pagi-debut-actif,
        .forum-live-hide-pagi-before .pagi-precedent-actif {
            display: none;
        }

        .forum-live-hide-pagi-after .pagi-suivant-actif {
            display: none;
        }

        .forum-live-activated #forum-live-button {
            border: 0.0625rem solid #c28507;
            background: #f0a100;
            color: #fff;
        }

        .forum-live-new-topic-1 {
            animation-duration:0.8s;
            animation-name: slidein-1;
        }

        .forum-live-new-topic-2 {
            animation-duration:0.8s;
            animation-name: slidein-2;
        }

        @keyframes slidein-1 {
            from {
              opacity: 0.1;
            }
            to {
              opacity: 1;
            }
        }

        @keyframes slidein-2 {
            from {
              opacity: 0.1;
            }
            to {
              opacity: 1;
            }
        }
    `;
    let css = `<style type="text/css" id="forum-live-css">${rules}</style>`;
    document.head.insertAdjacentHTML("beforeend", css);
}

function normalizeForumURL(url) {
    let regex = /^.*?\/\d+-(\d+)-\d+-\d+-\d+-\d+-\d+-(.*?)\.htm.*$/i;
    let [_, num, name] = url.match(regex);
    return `http://www.jeuxvideo.com/forums/0-${num}-0-1-0-1-0-${name}.htm`;
};

function process(dom, init) {
    requestsCounter++;
    let results = parseForum(dom);
    populateTopics(results);

    if (init) {
        display(currentMode, false, true);
        return;
    }

    if (isOver) {
        return;
    }

    let topicListClasses = document.getElementsByClassName("topic-list")[0].classList;
    topicListClasses.add("forum-live-tempo");

    timeoutId = setTimeout(function () {
        topicListClasses.remove("forum-live-tempo");
        display(currentMode, true, true);
    }, 1000);

};

function updateForum() {
    // Ensure that if "Live" button is clicked/unclicked, older request does not conflict with new one
    let currentId = intervalId;

    request(forumUrl, function (response) {
        if (currentId !== intervalId) {
            return;
        }
        process(response, false);
    });
};

function request(url, callback) {
    let xhr = new XMLHttpRequest();

    xhr.ontimeout = function () {
        console.error(`La délai d'attente de la requête a expiré`);
    };

    xhr.onerror = function () {
        console.error(`La requête a échoué (${xhr.status}): ${xhr.statusText}`);
    };

    xhr.onabort = function () {
        console.error(`La requête a été interrompue pour une raison inconnue`);
    };

    xhr.onload = function () {
        if (xhr.status !== 200) {
            console.error(`La requête a retourné une erreur (${xhr.status}): ${xhr.statusText}`);
            return;
        }
        callback(xhr.response);
    };

    xhr.responseType = "document";
    xhr.timeout = 5000;

    xhr.open("GET", url, true);
    xhr.setRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
    xhr.send();
};

function parseForum(document) {
    let connected = document.getElementsByClassName("nb-connect-fofo")[0];
    if (connected) {
        connected = parseInt(connected.textContent.trim());
    }
    let mps = document.getElementsByClassName("jv-account-number-mp")[0];
    if (mps) {
        mps = parseInt(mps.getAttribute("data-val"));
    }
    let notifs = document.getElementsByClassName("jv-account-number-notif")[0];
    if (notifs) {
        notifs = parseInt(notifs.getAttribute("data-val"));
    }
    let topicList = document.getElementsByClassName("topic-list")[0];
    let topics = [];
    for (let li of topicList.children) {
        if (!li.hasAttribute("data-id")) {
            continue;
        }
        let dataId = li.getAttribute("data-id");
        let topicImg = li.getElementsByClassName("topic-img")[0];
        let type = topicImg.title;
        let icon = topicImg.src;
        let topicTitle = li.getElementsByClassName("topic-title")[0];
        let title = topicTitle.title;
        let href = topicTitle.href;
        let topicAuthor = li.getElementsByClassName("topic-author")[0];
        let author = topicAuthor.textContent.trim();
        let authorClass = topicAuthor.className;
        let count = parseInt(li.getElementsByClassName("topic-count")[0].textContent.trim());
        let date = li.getElementsByClassName("topic-date")[0].textContent.trim();
        topics.push({ type: type, icon: icon, title: title, href: href, author: author, authorClass: authorClass, count: count, date: date, dataId: dataId });
    }
    return { connected: connected, topics: topics, mps: mps, notifs: notifs };
};

function update(element, topic) {
    let authorLink;
    if (topic.author === "Pseudo supprimé") {
        authorLink = `<span class="topic-author" style="font-style: italic;">Pseudo supprimé</span>`;
    } else {
        authorLink = `<a href="http://www.jeuxvideo.com/profil/${topic.author.toLowerCase()}?mode=infos" target="_blank" class="${topic.authorClass}">${topic.author}</a>`;
    }

    let lastPage = parseInt(topic.count / 20) + 1;
    let urlLastPage = topic.href.replace("-1-0-1-0-", `-${lastPage}-0-1-0-`);

    element.setAttribute("data-id", topic.dataId);
    let topicImg = element.getElementsByClassName("topic-img")[0];
    topicImg.src = topic.icon;
    topicImg.alt = topic.type;
    topicImg.title = topic.type;
    let topicTitle = element.getElementsByClassName("topic-title")[0];
    topicTitle.href = topic.href;
    topicTitle.title = topic.title;
    topicTitle.innerHTML = topic.title;
    element.getElementsByClassName("topic-author")[0].outerHTML = authorLink;
    element.getElementsByClassName("topic-count")[0].innerHTML = topic.count;
    let date = element.getElementsByClassName("topic-date")[0].getElementsByTagName("a")[0];
    date.href = urlLastPage;
    date.innerHTML = topic.date;
};

function populateTopics(results) {
    currentConnected = results.connected;
    nbMps = results.mps;
    nbNotifs = results.notifs;
    let topicsListIndex = 0;

    for (let topic of results.topics) {
        if (!dataTopics.hasOwnProperty(topic.dataId)) {
            let creationIndex = -1;
            if (topic.count <= 1) {
                creationIndex = requestsCounter + 1.0 / (topicsListIndex + 2);
            }
            dataTopic = { topic: topic, counts: [[requestsCounter, topic.count]], requestsCounter: requestsCounter, topicsListIndex: topicsListIndex, creationIndex: creationIndex, isNew: true };
            dataTopics[topic.dataId] = dataTopic;
        } else {
            dataTopic = dataTopics[topic.dataId];
            while (dataTopic.counts.length > 1 && requestsCounter - dataTopic.counts[0][0] > 60) {
                dataTopic.counts.shift();
            }
            dataTopic.isNew = false;
            dataTopic.topic = topic;
            dataTopic.counts.push([requestsCounter, topic.count]);
            dataTopic.requestsCounter = requestsCounter;
            dataTopic.topicsListIndex = topicsListIndex;
        }

        topicsListIndex++;
    }
};

function compareClassic(a, b) {
    if (a.requestsCounter > b.requestsCounter) {
        return -1;
    }
    if (a.requestsCounter < b.requestsCounter) {
        return 1;
    }
    if (a.topicsListIndex < b.topicsListIndex) {
        return -1;
    }
    if (a.topicsListIndex > b.topicsListIndex) {
        return 1;
    }
    return 0;
};

function compareNew(a, b) {
    if (a.creationIndex > b.creationIndex) {
        return -1;
    }
    if (a.creationIndex < b.creationIndex) {
        return 1;
    }
    if (a.topic.dataId > b.topic.dataId) {
        return -1;
    }
    if (a.topic.dataId < b.topic.dataId) {
        return 1;
    }
    return 0;
};

function compareHot(a, b) {
    let lenA = a.counts.length;
    let lenB = b.counts.length;

    let minA = a.counts[lenA - 1][1];
    let minB = b.counts[lenB - 1][1];
    let maxA = minA;
    let maxB = minB;

    for (let i = lenA - 2; i >= 0; i--) {
        let count = a.counts[i];
        if (requestsCounter - count[0] > 60) {
            break;
        }
        if (count[1] < minA) {
            minA = count[1];
        } else if (count[1] > maxA) {
            maxA = count[1];
        }
    }

    for (let i = lenB - 2; i >= 0; i--) {
        let count = b.counts[i];
        if (requestsCounter - count[0] > 60) {
            break;
        }
        if (count[1] < minB) {
            minB = count[1];
        } else if (count[1] > maxB) {
            maxB = count[1];
        }
    }

    let diffA = maxA - minA;
    let diffB = maxB - minB;


    if (diffA > diffB) {
        return -1;
    }
    if (diffB > diffA) {
        return 1;
    }

    let lastA = a.counts[lenA - 1][0];
    let lastB = b.counts[lenB - 1][0];

    if (lastA > lastB) {
        return -1;
    }
    if (lastB > lastA) {
        return 1;
    }

    if (a.topic.count > b.topic.count) {
        return -1;
    }
    if (b.topic.count > a.topic.count) {
        return 1;
    }
    return 0;
};

function slideIn(elem) {
    if (elem.classList.contains("forum-live-new-topic-1")) {
        elem.classList.remove("forum-live-new-topic-1");
        elem.classList.add("forum-live-new-topic-2");
    } else {
        elem.classList.remove("forum-live-new-topic-2");
        elem.classList.add("forum-live-new-topic-1");
    }
};

function display(mode, slidingIn, refresh) {
    document.getElementsByClassName("nb-connect-fofo")[0].innerHTML = `${currentConnected} connecté(s)`;

    if (nbMps !== undefined) {
        let mps = document.getElementsByClassName("jv-account-number-mp")[0];
        if (mps && parseInt(mps.getAttribute("data-val")) !== nbMps) {
            if (nbMps === 0) {
                mps.classList.remove("has-notif");
            } else {
                mps.classList.add("has-notif");
            }
            mps.setAttribute("data-val", nbMps);
        }
    }

    if (nbNotifs !== undefined) {
        let notifs = document.getElementsByClassName("jv-account-number-notif")[0];
        if (notifs && parseInt(notifs.getAttribute("data-val")) !== nbNotifs) {
            if (nbNotifs === 0) {
                notifs.classList.remove("has-notif");
            } else {
                notifs.classList.add("has-notif");
            }
            notifs.setAttribute("data-val", nbNotifs);
        }
    }

    let topics;

    if (refresh) {
        topics = Object.values(dataTopics);
        currentlyDisplayedTopics = JSON.parse(JSON.stringify(topics));
    } else {
        topics = currentlyDisplayedTopics;
    }

    if (mode == "classic") {
        topics.sort(compareClassic);
    } else if (mode == "new") {
        topics.sort(compareNew);
    } else if (mode == "hot") {
        topics.sort(compareHot);
    }

    let maxPage = parseInt((topics.length - 1) / topicsLength);
    let mainClasses = document.getElementById("forum-main-col").classList;

    if (displayedPage === 0) {
        mainClasses.add("forum-live-hide-pagi-before");
    } else {
        mainClasses.remove("forum-live-hide-pagi-before");
    }

    if (displayedPage === maxPage) {
        mainClasses.add("forum-live-hide-pagi-after");
    } else {
        mainClasses.remove("forum-live-hide-pagi-after");
    }

    let displayedTopics = topics.slice(displayedPage * topicsLength, (displayedPage + 1) * topicsLength);

    let changed = false;

    for (let i = displayedTopics.length - 1; i >= 0; i--) {
        let topic = displayedTopics[i];
        let elem = topicsDOM[i];
        elem.classList.remove("forum-live-hide");
        if (slidingIn) {
            let len = topic.counts.length;
            if (mode === "classic") {
                if (!changed && (len < 2 || (topic.counts[len - 1][1] > topic.counts[len - 2][1]))) {
                    changed = true;
                }
                if (changed && requestsCounter > 1 && topic.topic.type !== "Topic épinglé") {
                    slideIn(elem)
                }
            } else if (mode === "new") {
                if (topic.isNew && topic.creationIndex !== -1) {
                    topic.isNew = false;
                    slideIn(elem);
                }
            } else if (mode === "hot") {
                if (elem.getAttribute("data-id") !== topic.topic.dataId) {
                    slideIn(elem);
                }
            }
        } else {
            elem.classList.remove("forum-live-new-topic-1");
            elem.classList.remove("forum-live-new-topic-2");
        }

        update(elem, topic.topic);
    }

    for (let i = displayedTopics.length; i < topicsLength; i++) {
        topicsDOM[i].classList.add("forum-live-hide");
    }

    if (document.activeElement.classList.contains("lien-jv")) {
        document.activeElement.blur();
    }
};

function addForumLiveButton() {
    let button = `<button id="forum-live-button" class="btn btn-actu-new-list-forum btn-actualiser-forum">Live</button>`;
    let blocPreRight = document.getElementsByClassName("bloc-pre-right")[0];
    if (!blocPreRight) {
        console.error("Could not find 'Actualiser' button");
        return;
    }
    blocPreRight.insertAdjacentHTML("afterbegin", button);
}

function addForumLiveSelect() {
    let select = `
    <div class="forum-live-hide" id="forum-live-mode">
        <select id="forum-live-select" title="Choisir le critère de tri des topics">
            <option value="classic">Classique</option>
            <option value="new">Nouveau</option>
            <option value="hot">Tendance</option>
        </select>
    </div>
    `;
    let pagiBefore = document.getElementsByClassName("pagi-before")[0];
    if (!pagiBefore) {
        console.error("Could not find 'PagiBefore' button");
        return;
    }
    pagiBefore.insertAdjacentHTML("afterend", select);
}

function bindForumLiveSelect() {
    let select = document.getElementById("forum-live-select");
    select.addEventListener("change", changeMode);
}

function changeMode(event) {
    currentMode = event.target.value;
    display(currentMode, false, false);
};

function bindForumLiveButton() {
    let button = document.getElementById("forum-live-button");
    button.addEventListener("click", toggleForumLive);
}

function isActivated() {
    return document.getElementById("forum-main-col").classList.contains("forum-live-activated");
}

function toggleForumLive(event) {
    if (!initialized) {
        initialized = true;

        // TamperMonkey / Chrome bug: https://github.com/Tampermonkey/tampermonkey/issues/705#issuecomment-493895776
        if (window) {
            if (window.clearTimeout) {
                window.clearTimeout = window.clearTimeout.bind(window);
            }
            if (window.clearInterval) {
                window.clearInterval = window.clearInterval.bind(window);
            }
            if (window.setTimeout) {
                window.setTimeout = window.setTimeout.bind(window);
            }
            if (window.setInterval) {
                window.setInterval = window.setInterval.bind(window);
            }
        }

        for (let elem of document.getElementsByClassName("topic-list")[0].children) {
            if (!elem.hasAttribute("data-id")) {
                continue;
            }
            elem.className = "";  // Remove blue overlay for deleted topic
            topicsDOM.push(elem);
        }
        topicsLength = topicsDOM.length;

        forumUrl = normalizeForumURL(document.URL);

        addForumLiveSelect();
        bindForumLiveSelect();

        document.getElementsByClassName("conteneur-topic-pagi")[0].addEventListener("mouseenter", function (event) {
            isOver = true;
            clearTimeout(timeoutId);
            document.getElementsByClassName("topic-list")[0].classList.remove("forum-live-tempo");
            timeoutId = undefined;
        })

        document.getElementsByClassName("conteneur-topic-pagi")[0].addEventListener("mouseleave", function (event) {
            isOver = false;
        });

        for (let elem of document.getElementsByClassName("pagi-before")) {
            elem.innerHTML = `<span><a href="${forumUrl}" class="pagi-debut-actif"><span>Début</span></a></span><span><a href="${forumUrl}" class="pagi-precedent-actif"><span>Page précédente</span></a></span>`;
        }

        for (let elem of document.getElementsByClassName("pagi-after")) {
            pagiAfterSaved = elem.innerHTML;
            elem.innerHTML = `<span><a href="${forumUrl.replace("-1-0-1-0-", "-1-0-26-0-")}" class="pagi-suivant-actif"><span>Page suivante</span></a></span>`;
        }

        function pageDebut(event) {
            if (!isActivated()) {
                return;
            }
            event.preventDefault();
            displayedPage = 0;
            display(currentMode, false, false);
        };

        function pagePrecedent(event) {
            if (!isActivated()) {
                return;
            }
            event.preventDefault();
            displayedPage--;
            display(currentMode, false, false);
        };

        function pageSuivant(event) {
            if (!isActivated()) {
                return;
            }
            event.preventDefault();
            displayedPage++;
            display(currentMode, false, false);
        }

        for (let elem of document.getElementsByClassName("pagi-debut-actif")) {
            elem.addEventListener("click", pageDebut);
        }

        for (let elem of document.getElementsByClassName("pagi-precedent-actif")) {
            elem.addEventListener("click", pagePrecedent);
        }

        for (let elem of document.getElementsByClassName("pagi-suivant-actif")) {
            elem.addEventListener("click", pageSuivant);
        }

    }

    document.getElementById("forum-main-col").classList.toggle("forum-live-activated");
    let button = document.getElementById("forum-live-button");
    let mode = document.getElementById("forum-live-mode");
    mode.classList.toggle("forum-live-hide");

    if (isActivated()) {
        for (let topic of topicsDOM) {
            for (let link of topic.getElementsByClassName("lien-jv")) {
                link.setAttribute("target", "_blank");
            }
        }
        process(document, true);
        intervalId = setInterval(updateForum, 5000);
    } else {
        clearTimeout(intervalId);
        intervalId = undefined;
        displayedPage = 0;
        display("classic", false, true);
        dataTopics = {};
        requestsCounter = 0;
        document.getElementById("forum-main-col").classList.remove("forum-live-hide-pagi-after");
        document.getElementById("forum-main-col").classList.add("forum-live-hide-pagi-before");
        document.getElementsByClassName("topic-list")[0].classList.remove("forum-live-tempo");
        for (let topic of topicsDOM) {
            for (let link of topic.getElementsByClassName("lien-jv")) {
                link.removeAttribute("target");
            }
        }
    }
}

function main() {
    addCss();
    addForumLiveButton();
    bindForumLiveButton();
}

main();