ČSFD Compare

Show your own ratings on other users ratings list

目前為 2021-05-30 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         ČSFD Compare
// @version      0.3.4.1
// @namespace    csfd.cz
// @description  Show your own ratings on other users ratings list
// @author       Jan Verner <[email protected]>
// @icon         http://img.csfd.cz/assets/b1733/images/apple_touch_icon.png
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @include      *csfd.cz/*
// ==/UserScript==

Glob = {
    popupCounter: 0,

    popup: function (htmlContent, timeout = 3, width = 150) {
        var id = Glob.popupCounter++;
        if (!htmlContent) {
            return;
        }
        var yOffset = 10;
        $(".header-search").append(`
            <div class='SNPopup' id='SNPopup${id}'
                style='
                    border: 1px solid black;
                    border-radius:4px;
                    display:none;
                    padding:10px;
                    opacity:0.95;
                    background:#820001;
                    color:white;
                    position:absolute;
                    left:45%;
                    width:${width}px;
                    z-index:999;
                    top:${yOffset}px;
                    right:10px'
            >${htmlContent}</div>`);
        var $me = $(`#SNPopup${id}`);
        $me.slideDown(100);
        (function (id) {
            setTimeout(function () {
                $(`#SNPopup${id}`).slideUp(100);
            }, timeout * 1000);
        })(id);
    }
};

(async () => {
    "use strict";
    /* globals jQuery, $, waitForKeyElements */
    /* jshint -W069 */

    // const delay = ms => new Promise(res => setTimeout(res, ms));
    const SCRIPTNAME = 'CSFD Compare';

    class Csfd {

        constructor(csfdPage) {
            this.csfdPage = csfdPage;
            this.stars = {};
            this.storageKey = undefined;
            this.userUrl = undefined;
            this.endPageNum = 0;
            this.userRatingsCount = 0;
            this.userRatingsUrl = undefined;
            this.localStorageRatingsCount = 0;

            // Ignore the ads... Make 'hodnoceni' table wider.
            $('.column.column-80').attr('class', '.column column-90');
        }

        getEndPageNum(data) {
            console.log("fn: getEndPageNum()");
            let $pagination = $(data).find('.box-content').find('.box-more-bar').find('.pagination')[0];
            let lastPageHref = $($pagination).find('a:nth-last-child(2)').attr('href');
                let foundMatch = lastPageHref.match(new RegExp("page=(.*)$"));

            let endPageNum = 0;
                if (foundMatch.length == 2) {
                endPageNum = parseInt(foundMatch[1]);
                }
            console.log("  return:", endPageNum);
            return endPageNum;
        }

        getCurrentUser() {
            console.log("fn getCurrentUser()");

            let loggedInUser = $('.profile.initialized').attr('href');
            if (typeof loggedInUser !== 'undefined') {
            if (loggedInUser.length == 1) {
                loggedInUser = loggedInUser[0];
            }
            }
            console.log("loggedInUser:", loggedInUser);

            if (typeof loggedInUser === 'undefined') {
                console.log("Trying again...");

                // [OLD Firefox] workaround (the first returns undefined....?)
                let profile = document.querySelectorAll('.profile');
                if (profile.length == 0) {
                    return undefined;
                }
                loggedInUser = profile[0].getAttribute('href');
                console.log(`loggedInUser: ${loggedInUser}`);

                if (typeof loggedInUser === 'undefined') {
                    console.error(`${SCRIPTNAME}: Can't find logged in username...`);
                    throw (`${SCRIPTNAME}: exit`);  // TODO: Popup informing user
                }
            }
            return loggedInUser;
        }

        getStars() {
            if (localStorage[this.storageKey]) {
                let stars = JSON.parse(localStorage[this.storageKey]);
                return stars;
            }
        }

        getCurrentFilmUrl() {
            console.log("fn: getCurrentFilmUrl()");

            // Find "Diskuze" button and from it's a href extract /film/${URL}/diskuze
            let navItemHref = this.csfdPage.find('.tab-nav-item-9 > a').attr('href');

            let foundMatch = navItemHref.match(new RegExp("film/" + "(.*)" + "/diskuze"));
            if (foundMatch == null) {
                console.error("TODO: nenaslo to... vyhledat jinym zpusobem!");
                throw (`${SCRIPTNAME} Exiting...`);
            }

            let filmUrl = `/film/${foundMatch[1]}/`;
            return filmUrl;
        }

        updateInLocalStorage(ratingNum) {
            console.log("Checking if in LocalStorage...");

            // Check if film is in LocalStorage
            let filmUrl = this.getCurrentFilmUrl();
            console.log("filmUrl:", filmUrl);
            let item = this.stars[filmUrl];

            // Item not in LocalStorage, add it then!
            if (typeof item === 'undefined') {
                console.log(`Item not in LocalStorage, adding... ${filmUrl}: ${ratingNum}`);
                this.stars[filmUrl] = ratingNum;
                localStorage.setItem(this.storageKey, JSON.stringify(this.stars));
                return true;
            }

            if (item != ratingNum) {
                console.log(`LocalStorage rating [${item}] != current rating [${ratingNum}], updating...`);
                this.stars[filmUrl] = ratingNum;
                localStorage.setItem(this.storageKey, JSON.stringify(this.stars));
                return true;
            }

            console.log(`Rating same in LocalStorage, everything ok`);
            return true;
        }

        removeFromLocalStorage() {
            console.log("fn: removeFromLocalStorage()");
            console.log("  Deleting item from LocalStorage...");

            // Check if film is in LocalStorage
            let filmUrl = this.getCurrentFilmUrl();
            let item = this.stars[filmUrl];

            // Item not in LocalStorage, everything is fine
            if (typeof item === 'undefined') {
                console.log("  Item not in LocalStorage, nothing happens");
                return null;
            }

            // Item in LocalStorage, delete it from local dc
            delete this.stars[filmUrl];

            // And resave it to LocalStorage
            console.log("  Resaving ratings into LocalStore");
            localStorage.setItem(this.storageKey, JSON.stringify(this.stars));

            return true;
        }

        checkLocalStorageRatings() {
            console.log("fun: checkLocalStorageRatings()");
            //TODO: Duplicitni, nejak to sloucit...
            console.log("LOADING RATINGS...");

            this.userUrl = this.getCurrentUser();
            console.log("this.userUrl:", this.userUrl);

            this.storageKey = "CsfdCompare_" + this.userUrl.split("/")[2].split("-")[1];
            console.log("this.storageKey", this.storageKey);

            // Try cache
            if (localStorage[this.storageKey]) {
                this.stars = JSON.parse(localStorage[this.storageKey]);
            }

            if (this.stars != {}) {
                return true;
            }

            return false;
            // // Cache does not exists...
            // if (Object.keys(this.stars).length == 0) {
            //     Glob.popup("Načítam hodnocení...");
            //     // TODO: Add floating pregress notification {loading... 354/2049}
            //     this.refresh();
            // }

        }

        getCurrentFilmRating() {
            let $currentUserRating = this.csfdPage.find('.current-user-rating .stars');

            // Exit if no user rating found
            if ($currentUserRating.length == 0) {
                return null;
            }

            // Ignore 'computed' ratings (exit)
            if ($currentUserRating.parent().hasClass('computed')) {
                console.log("Smula, computed....");
                return null;
            }

            // Find the rating
            for (let num = 0; num <= 5; num++) {
                if ($currentUserRating.hasClass(`stars-${num}`)) {
                    return num;
                }
            }

            // If not numeric rating, return 0 (Odpad!)
            if ($currentUserRating.find('.trash').length > 0) {
                return 0;
            }
        }


        refresh() {
            let url = this.userUrl + "hodnoceni/";
            console.log(`REFRESHING... ${url}`);
            this.loadHodnoceniPage(url);

        }

        getCurrentUserRatingsCount() {
            console.log(`fn: getCurrentUserRatingsCount()`);
            let count = 0;
            let request = $.ajax({
                type: "GET",
                url: this.userRatingsUrl,
                async: false
            });
            request.done((data) => {
                // Get ratings: '(2 403)'
                let $countSpan = $(data).find('span.count');
                console.log(`  $countSpan: ${$countSpan}`);
                if ($countSpan.length == 1) {
                    // Strip it '(2 403)' --> '2403'
                    count = $countSpan[0].innerText.replace('(', '').replace(')', '').replace(/ +/g, '').replace(/\xA0/g, '');
                    count = parseInt(count);
                }
            });
            console.log(`  Returning count: ${count}`);
            return count;
        }

        getLocalStorageRatingsCount() {
            console.log(`fn: getLocalStorageRatingsCount()`);
            // this.storageKey = "CsfdCompare_" + this.userUrl.split("/")[2].split("-")[1];
            console.log("  this.storageKey:", this.storageKey);
            if (localStorage[this.storageKey]) {
                let stars = JSON.parse(localStorage[this.storageKey]);
                let count = Object.keys(stars).length;
                console.log("  return:", count);
                return count;
            }
            return 0;

        }

        onOtherUserHodnoceniPage() {
            if (location.href.includes('/hodnoceni') && location.href.includes('/uzivatel/')) {
                if (!location.href.includes(this.userUrl)) {
                    return true;
                }
            }
            return false;
        }

        exportRatings() {
            console.log(`fn: exportRatings()`);
            console.log("  this.storageKey:", this.storageKey);
            // console.log("JSON.stringify(this.stars):", JSON.stringify(this.stars));
            localStorage.setItem(this.storageKey, JSON.stringify(this.stars));
            if (this.onOtherUserHodnoceniPage()) {
                this.addRatingsColumn();
            }
        }

        importRatings() {
            console.log(`fn: importRatings()`);
            if (localStorage[this.storageKey]) {
                this.stars = JSON.parse(localStorage[this.storageKey]);
            }
        }

        async REFRESH_RATINGS() {
            // Load user ratings...
            let $this = this;
            return new Promise((resolve, reject) => {
                console.log("fn: REFRESH_RATINGS()");
                console.log(`  Getting data from: '${$this.userRatingsUrl}'...`);
                $.ajax({
                type: "GET",
                    url: $this.userRatingsUrl,
                async: true
                }).done((data) => {
                    // Get how many pages will the script load
                    $this.endPageNum = $this.getEndPageNum(data);
                    console.log("  $this.endPageNum:", $this.endPageNum);
                    this.processRatingsPage(data);
                    resolve();
            });
            });
        }

        async processRatingsPage(dataHTML) {
            if (!dataHTML) {
                return;
            }

            var $stars = this.stars;
            $(dataHTML).find("tbody tr").each(function () {
                var $row = $(this);
                var filmURL = $("a.film-title-name", $row).attr("href");
                var $rating = $("span .stars", $row);

                let starsRating = 0;
                for (let stars = 0; stars <= 5; stars++) {
                    if ($rating.hasClass('stars-' + stars)) {
                        starsRating = stars;
                    }
                }
                // Add to dict
                $stars[filmURL] = starsRating;
            });

            // Check if there is next page
            let nextPaginationURL = $(dataHTML).find("a.page-next").attr("href");
            if (nextPaginationURL) {
                // Next page exists, fetch it and repeat this function, add new ratings to `this.stars`
                await this.loadPage(nextPaginationURL);
            } else {
                // No next page, finish...
                this.finishRefresh();
            }
        }

        async loadPage(url) {
            console.log(`fn: loadPage(${url})`);
            return new Promise((resolve, reject) => {
                let foundMatch = url.match(new RegExp("page=(.*)$"));

                let currentNum = 1;
                if (foundMatch.length == 2) {
                    currentNum = foundMatch[1];
                }

                Glob.popup(`${SCRIPTNAME} - Nacitam... ${currentNum}/${this.endPageNum}`);

                $.ajax({
                    type: "GET",
                    url: url,
                    async: true
                }).done((data) => {
                    this.processRatingsPage(data);
                    resolve();
                });
            });
        }

        finishRefresh() {
            console.log("fn: finishRefresh()");
            this.exportRatings();
            console.log("  Hotovo, hodnocení načteno");
            Glob.popup(`Vaše hodnocení byla načtena.`);
        }

        addRatingsColumn() {
            console.log("fn: addRatingsColumn()");
            let $page = this.csfdPage;

            let $tbl = $page.find('#snippet--ratings table tbody');
            let starsDict = this.getStars();

            $tbl.find('tr').each(function () {
                let $row = $(this);
                let url = $($row).find('.name').find('a').attr('href');
                let ratingNum = starsDict[url];

                let $span = "";
                if (ratingNum == 0) {
                    $span = `<span class="stars trash">odpad!</span>`;
                }
                else {
                    $span = `<span class="stars stars-${ratingNum}"></span>`;
                }

                $row.find('td:nth-child(2)').after(`
                    <td class="star-rating-only">
                        <span class="star-rating">
                            ${$span}
                        </span>
                    </td>
                `);
            });
        }

        createRefreshButton() {
            let button = document.createElement("button");
            button.innerHTML = `<span style="text-transform: initial;">CSFD-Compare reload<br>${this.localStorageRatingsCount}/${this.userRatingsCount}</span>`;
            button.className = "csfd-compare-reload";
            // button.setAttribute("style", "margin-top: 3px; margin-left: 20px; align: right; font-size: 0.7em;");

            // Add at the end of the user main-menu
            let menu = document.getElementsByClassName("main-menu")[0];
            menu.insertBefore(button, menu.lastChild);

            // Add event to refresh all rating into LocalStorage on click
            $(button).on("click", function () {
                let csfd = new Csfd($('div.page-content'));
                csfd.userUrl = csfd.getCurrentUser();
                csfd.userRatingsUrl = `${csfd.userUrl}/hodnoceni`;
                csfd.storageKey = `${SCRIPTNAME}_${csfd.userUrl.split("/")[2].split("-")[1]}`;
                csfd.REFRESH_RATINGS();
            });
        }

        openControlPanelOnHover() {
            let btn = $('.button-control-panel');
            let panel = $('#dropdown-control-panel');

            $(btn).on('hover mouseover', function () {
                if (!panel.hasClass('active')) {
                    panel.addClass('active');
                }
            });

            $(panel).on('hover mouseover', function () {
                if (!panel.hasClass('active')) {
                    panel.addClass('active');
                }
            });

            $(panel).on('mouseleave', function () {
                if (panel.hasClass('active')) {
                    // setTimeout(function () {
                    panel.removeClass('active');
                    // }, 500);
                }
            });

            $(btn).on('mouseleave', function () {
                if (panel.hasClass('active')) {
                    // setTimeout(function () {
                    panel.removeClass('active');
                    // }, 500);
                }
            });
        }

        hideControlPanel() {
            let btn = $('.button-control-panel');
            btn.addClass('hidden');
        }
    }


    // SCRIPT START
    let csfd = new Csfd($('div.page-content'));

    csfd.userUrl = csfd.getCurrentUser();

    // Either logged in or not, do this...


    // If not logged in, hide control panel
    if (csfd.userUrl === undefined) {
        csfd.hideControlPanel();
    }

    // If logged in, do some stuff
    if (csfd.userUrl !== undefined) {

        csfd.storageKey = `${SCRIPTNAME}_${csfd.userUrl.split("/")[2].split("-")[1]}`;
        csfd.userRatingsUrl = `${csfd.userUrl}/hodnoceni`;
        csfd.stars = csfd.getStars();

        if (location.href.includes('/film/')) {
            let currentFilmRating = csfd.getCurrentFilmRating();
            console.log("currentFilmRating:", currentFilmRating);
            if (currentFilmRating == null) {
                // Check if record exists, if yes, remove it
                csfd.removeFromLocalStorage();
            } else {
                // Check if current page rating corresponds with that in LocalStorage, if not, update it
                csfd.updateInLocalStorage(csfd.getCurrentFilmRating());
            }
        }
        csfd.userRatingsCount = csfd.getCurrentUserRatingsCount();
        csfd.localStorageRatingsCount = csfd.getLocalStorageRatingsCount();

        csfd.openControlPanelOnHover();
        csfd.createRefreshButton();

        // Show user that his 'user ratings' and 'local storage ratings' are not the same and he should refresh
        let ratingsCountOk = csfd.userRatingsCount == csfd.localStorageRatingsCount;
        console.log("ratingsCountOk:", ratingsCountOk);

        if (!ratingsCountOk) {
            console.warn(`Current ${csfd.userRatingsCount} != LocalStorage ${csfd.localStorageRatingsCount}.`);
            Glob.popup(`
                ${SCRIPTNAME}: Je třeba obnovit hodnocení<br>
                - váš počet: ${csfd.userRatingsCount}<br>
                - uloženo v prohlížeči: ${csfd.localStorageRatingsCount}<br>
                <b>Nastavení uživatele --> CSFD-Compare reload</b>`, 8, 310);
    }

        console.log("ratingsCountOk:", ratingsCountOk);
        if (ratingsCountOk) {
            // Show user ratings on any other user but mine
            if (csfd.onOtherUserHodnoceniPage()) {
                csfd.addRatingsColumn();
            }
        }
    }

})();