NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Shiki Rating // @namespace http://shikimori.one/ // @version 2.7.3 // @description Добавляет рейтинг произведений на основе оценок пользователей шики (также при наведении не учитываются низкие оценки) // @author Chortowod // @match *://shikimori.org/* // @match *://shikimori.one/* // @match *://shikimori.me/* // @icon https://www.google.com/s2/favicons?domain=shikimori.one // @license MIT // @require https://gist.githubusercontent.com/arantius/3123124/raw/grant-none-shim.js // @copyright 2024, Chortowod (https://openuserjs.org/users/Chortowod) // @updateURL https://openuserjs.org/meta/Chortowod/Shiki_Rating.meta.js // @downloadURL https://openuserjs.org/install/Chortowod/Shiki_Rating.user.js // @require https://gist.githubusercontent.com/Chortowod/814b010c68fc97e5f900df47bf79059c/raw/chtw_settings.js?v1 // @grant none // ==/UserScript== let settings = new ChtwSettings('chtwRating', '<a target="_blank" href="https://openuserjs.org/scripts/Chortowod/Shiki_Rating">Рейтинг шикимори</a>'); let debug; function initSettings() { settings.createOption('isDebug', 'Отладка', false); settings.createOption('lowest', 'Минимальный порог', 4, 'number'); settings.createOption('starColor', 'Цвет звезд', '#a22123', 'color'); settings.createOption('textColor', 'Цвет надписи', '#7b8084', 'color'); debug = settings.getOption('isDebug'); } function log(message) { if (debug) { console.log('Shiki Rating:'); console.log(message); } } function getLocale() { return document.querySelector('body').getAttribute('data-locale'); } function getLabelData() { return getLocale() === 'ru' ? {"0":"Невыносимо","1":"Хуже некуда","2":"Ужасно","3":"Очень плохо","4":"Плохо","5":"Более-менее","6":"Нормально","7":"Хорошо","8":"Отлично","9":"Великолепно","10":"Эпик вин!"} : {"0":"Unbearable","1":"Worst Ever","2":"Terrible","3":"Very Bad","4":"Bad","5":"So-so","6":"Fine","7":"Good","8":"Excellent","9":"Great","10":"Masterpiece!"}; } function removeLastClass(domElement) { let classes = domElement.classList; classes.remove(classes.item(classes.length - 1)); } function createElementFromHTML(htmlString) { var div = document.createElement('div'); div.innerHTML = htmlString.trim(); return div.firstChild; } function calculateShikiRating(scoreData, lowestRate = 0) { let sumScore = 0; let totalCount = 0; for (let i = 0; i < scoreData.length; i++) { if (scoreData[i][0] >= lowestRate) { sumScore += parseInt(scoreData[i][1]) * scoreData[i][0]; totalCount += parseInt(scoreData[i][1]); } } log(sumScore); log(totalCount); let shikiScore = sumScore / totalCount; if (lowestRate) log('Оценка, не учитывая меньше '+lowestRate+': '+shikiScore); else log('Оценка: '+shikiScore); if (!shikiScore) return false; else return {'score': shikiScore.toFixed(2), 'scoreMath': Math.round(shikiScore), 'count': totalCount}; } function scoreOnHover(eventType, score, total) { let shikiScoreElement = document.getElementById("shiki-score"); let dataScore = shikiScoreElement.getAttribute(score); shikiScoreElement.addEventListener(eventType, function( event ) { shikiScoreElement.querySelector('.text-score .score-value').innerHTML = dataScore; let starElement = shikiScoreElement.querySelector("div.stars-container > div.stars.score"); removeLastClass(starElement); starElement.classList.add("score-" + Math.round(dataScore)); document.querySelector('.score-counter > strong').textContent = shikiScoreElement.getAttribute(total); shikiScoreElement.querySelector("div.text-score > div.score-notice").textContent = getLabelData()[Math.round(dataScore)]; }, false); } function appendShikiRating() { 'use strict'; let currentPath = window.location.pathname.substring(0, 7); if (!(currentPath === "/animes" || currentPath === "/mangas" || currentPath === "/ranobe") || !document.querySelector(".c-info-right")) { log('Неподходящая страница'); return; } if (document.querySelector("#shiki-score") !== null) { log('Уже был создано'); scoreOnHover('mouseover', 'data-score-low', 'data-count-low'); scoreOnHover('mouseout', 'data-score', 'data-count'); return; } let newShikiRate, malRate; if (document.querySelector(".scores > .b-rate") === null) { // создаем контейнер с оценками вручную, если его нет let rateFromZero = createElementFromHTML('<div class="block" itemprop="aggregateRating" itemscope="" itemtype="http://schema.org/AggregateRating"><div class="subheadline m5">Рейтинг</div><div class="scores"><div class="b-rate" id="shiki-score"><div class="stars-container"><div class="hoverable-trigger"></div><div class="stars score score-9" style="color: rgb(162, 33, 35);"></div><div class="stars hover"></div><div class="stars background"></div></div><div class="text-score"><div class="score-value score-9"></div><div class="score-notice"></div></div></div><p class="score-counter" style="text-align: center; color: rgb(123, 128, 132);"></p></div></div>'); document.querySelector(".c-info-right").prepend(rateFromZero); newShikiRate = document.getElementById('shiki-score'); } else { // создаем контейнер с оценками по подобию уже имеющегося malRate = document.querySelector(".scores > .b-rate"); malRate.setAttribute('id', 'mal-score'); newShikiRate = malRate.cloneNode(true); newShikiRate.setAttribute('id', 'shiki-score'); document.querySelector(".scores").appendChild(newShikiRate); } // подгружаем оценки let scoreDataJson = document.querySelector("#rates_scores_stats").getAttribute("data-stats"); let scoreData = JSON.parse(scoreDataJson); // если оценок не оказалось if (!scoreData || scoreData.length === 0) { newShikiRate.innerHTML = getLocale() === 'ru' ? 'Недостаточно данных' : 'Insufficient data'; newShikiRate.style.textAlign = 'center'; newShikiRate.style.marginTop = '30px'; return; } // считаем рейтинг let shikiRateData = calculateShikiRating(scoreData); let shikiRateLowData = calculateShikiRating(scoreData, settings.getOption('lowest')) || shikiRateData; // добавляем данные в аттрибуты для js-событий let shikiRateDiv = document.getElementById('shiki-score'); shikiRateDiv.setAttribute('data-score', shikiRateData.score); shikiRateDiv.setAttribute('data-score-low', shikiRateLowData.score); shikiRateDiv.setAttribute('data-count', shikiRateData.count); shikiRateDiv.setAttribute('data-count-low', shikiRateLowData.count); // меняем значение оценки let scoreElement = newShikiRate.querySelector("div.text-score > div.score-value"); scoreElement.innerHTML = shikiRateData.score; removeLastClass(scoreElement); scoreElement.classList.add("score-" + Math.round(shikiRateData.score)); // меняем количество звезд let starElement = newShikiRate.querySelector("div.stars-container > div.stars.score"); removeLastClass(starElement); starElement.style.color = settings.getOption('starColor'); starElement.classList.add("score-" + shikiRateData.scoreMath); // меняем тип оценки (хорошо, плохо и т.п.) newShikiRate.querySelector("div.text-score > div.score-notice").textContent = getLabelData()[shikiRateData.scoreMath]; // добавляем информацию к стандартной оценке, что это mal if (document.getElementById('mal-score')) { let malLabel = getLocale() === 'ru' ? 'На основе оценок MAL' : 'From MAL users'; malRate.insertAdjacentHTML('afterend', '<p class="score-source">' + malLabel + '</p>'); let malScoreLabelElement = document.querySelector('.score-source'); malScoreLabelElement.style.marginBottom = '15px'; malScoreLabelElement.style.textAlign = 'center'; malScoreLabelElement.style.color = settings.getOption('textColor'); } // добавляем информацию к шики оценке, что это шики let shikiCountLabel = '<strong>' + shikiRateData.count + '</strong>'; shikiCountLabel = (getLocale() === 'ru') ? 'На основе ' + shikiCountLabel + ' оценок Shikimori' : 'From ' + shikiCountLabel + ' shiki users'; newShikiRate.insertAdjacentHTML('afterend', '<p class="score-counter">' + shikiCountLabel + '</p>'); let shikiScoreLabelElement = document.querySelector('.score-counter'); shikiScoreLabelElement.style.textAlign = 'center'; shikiScoreLabelElement.style.color = settings.getOption('textColor'); // при наведении показывает оценку, не учитывая низкие scoreOnHover('mouseover', 'data-score-low', 'data-count-low'); scoreOnHover('mouseout', 'data-score', 'data-count'); } function ready(fn) { document.addEventListener('page:load', fn); document.addEventListener('turbolinks:load', fn); if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") fn(); else document.addEventListener('DOMContentLoaded', fn); } ready(initSettings); ready(appendShikiRating);