Accurately sorts your anime list by progress, and displays correct progress percentages for each anime
- // ==UserScript==
- // @name MAL (MyAnimeList) - Accurate Progress & Sorting On Anime List
- // @version 3.2
- // @author Ezektor
- // @description Accurately sorts your anime list by progress, and displays correct progress percentages for each anime
- // @match https://myanimelist.net/animelist/*
- // @grant none
- // @icon https://myanimelist.net/favicon.ico
- // @namespace https://greasyfork.org/users/1414348
- // ==/UserScript==
- (function() {
- 'use strict';
- let isSortingEnabled = false;
- let originalRows = [];
- let sortDirection = 'desc';
- const toggleButton = document.createElement('button');
- toggleButton.innerHTML = '➖ Default';
- toggleButton.style.position = 'fixed';
- toggleButton.style.bottom = '20px';
- toggleButton.style.right = '20px';
- toggleButton.style.padding = '10px';
- toggleButton.style.backgroundColor = '#9e9e9e';
- toggleButton.style.color = 'white';
- toggleButton.style.border = 'none';
- toggleButton.style.borderRadius = '5px';
- toggleButton.style.cursor = 'pointer';
- toggleButton.style.zIndex = '9999';
- document.body.appendChild(toggleButton);
- function isAllAnimePage() {
- return window.location.href.includes('/animelist/') && window.location.href.includes('?status=7');
- }
- function calculateProgress(row) {
- const progressCell = row.querySelector('.data.progress');
- if (!progressCell) return 0;
- const progressText = progressCell.innerText.trim();
- const [watched, total] = progressText.replace(/\[[^\]]*\]/g, '').split('/').map(s => parseInt(s.trim(), 10) || 0);
- return total == null ? 100 : total === 0 ? 0 : Math.floor((watched / total) * 100);
- }
- function addProgressPercentage(row) {
- const progressCell = row.querySelector('.data.progress');
- if (!progressCell) return;
- const progressText = progressCell.innerText.trim();
- if (/^\d+$/.test(progressText)) return;
- const existingPercentage = progressCell.querySelector('.progress-percentage')
- if (existingPercentage) {
- existingPercentage.remove();
- }
- const progressPercentage = calculateProgress(row);
- const progressPercentageElement = document.createElement('div');
- progressPercentageElement.style.fontSize = '12px';
- progressPercentageElement.style.marginTop = '5px';
- progressPercentageElement.classList.add('progress-percentage');
- progressPercentageElement.textContent = `${progressPercentage}% progress`;
- if (progressPercentage <= 33) {
- progressPercentageElement.style.color = '#f44336';
- } else if (progressPercentage <= 66) {
- progressPercentageElement.style.color = '#ff9800';
- } else {
- progressPercentageElement.style.color = '#4caf50';
- }
- progressCell.appendChild(progressPercentageElement);
- }
- function sortRowsByProgress(rows, direction) {
- return [...rows].sort((a, b) => {
- const progressA = calculateProgress(a);
- const progressB = calculateProgress(b);
- const isCompletedA = a.querySelector('.data.status').classList.contains('completed');
- const isCompletedB = b.querySelector('.data.status').classList.contains('completed');
- const isDroppedA = a.querySelector('.data.status').classList.contains('dropped');
- const isDroppedB = b.querySelector('.data.status').classList.contains('dropped');
- if (isAllAnimePage()) {
- if (isDroppedA && !isDroppedB) return 1;
- if (!isDroppedA && isDroppedB) return -1;
- if (isCompletedA && !isCompletedB) return direction === 'desc' ? -1 : 1;
- if (!isCompletedA && isCompletedB) return direction === 'desc' ? 1 : -1;
- }
- return direction === 'asc'
- ? progressA - progressB
- : progressB - progressA;
- });
- }
- function updateTable(sortedRows) {
- const table = document.querySelector('table');
- const tbody = table.querySelector('tbody.list-item');
- tbody.innerHTML = '';
- sortedRows.forEach(row => tbody.appendChild(row));
- }
- function restoreOriginalOrder() {
- const table = document.querySelector('table');
- const tbody = table.querySelector('tbody.list-item');
- tbody.innerHTML = '';
- originalRows.forEach(row => tbody.appendChild(row));
- }
- function storeOriginalRows() {
- const rows = document.querySelectorAll('tbody.list-item .list-table-data');
- originalRows = [...rows];
- }
- function main() {
- const rows = document.querySelectorAll('tbody.list-item .list-table-data');
- rows.forEach(addProgressPercentage);
- if (isSortingEnabled) {
- const sortedRows = sortRowsByProgress(rows, sortDirection);
- updateTable(sortedRows);
- }
- }
- toggleButton.addEventListener('click', () => {
- if (!isSortingEnabled) {
- isSortingEnabled = true;
- sortDirection = 'desc';
- toggleButton.style.backgroundColor = '#4CAF50';
- toggleButton.innerHTML = 'Sorting In: 📉 Descending Order';
- main();
- } else if (sortDirection === 'desc') {
- sortDirection = 'asc';
- toggleButton.innerHTML = 'Sorting In: 📈 Ascending Order';
- main();
- } else {
- isSortingEnabled = false;
- toggleButton.style.backgroundColor = '#9e9e9e';
- toggleButton.innerHTML = '➖ Default';
- restoreOriginalOrder();
- }
- });
- window.addEventListener('load', () => {
- storeOriginalRows();
- main();
- });
- })();