MyAnimeList(MAL) - Extra

Show anime/manga info inside your animelist/mangalist

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         MyAnimeList(MAL) - Extra
// @version      5.2.0
// @description  Show anime/manga info inside your animelist/mangalist
// @author       Cpt_mathix
// @match        *://myanimelist.net/animelist/*
// @match        *://myanimelist.net/mangalist/*
// @license      GPL-2.0-or-later
// @namespace    https://greasyfork.org/users/16080
// ==/UserScript==

(function ($) {
	var store = (function () {
		var map = {};

		return {
			set: function (key, value) {
				map[key] = value;
			},
			get: function (key) {
				return map[key];
			}
		};
	})();

	var mal = {
		href: document.location.href,
		modern: false,
		user: '',
		type: []
	};

	init();

	function init() {
		mal.user = mal.href.match(/[^/]*?(?=\?|$)/)[0];

		if (mal.href.indexOf("mangalist") > -1) {
			mal.type = ["manga", 65];
		} else {
			mal.type = ["anime", 64];
		}

		mal.modern = $('.header .header-menu .btn-menu > .username').length > 0;

		if (mal.modern) {
			initGlobalScrollListener();
			try {
				setTimeout(initModernList, 1000);
			} catch (ex) {
				console.log(ex);
			}
		} else {
			initOldList();
		}
	}

	function initOldList() {
		$('#list_surround > table > tbody > tr > td[class*=td]:nth-child(2)').each(function () {
			var id = $(this).find('> a').attr('href').match(/\/(\d+)\//)[1];
			var tdtype = $(this).attr('class').match(/\d/)[0];
			var moreEl = $(this).find('> div > small > a:nth-child(2)');
			var memberId = $('#listUserId').val();
			var moreObject = $("#more" + id);

			$(moreEl).removeAttr('onclick');
			$(moreEl).attr("href", "javascript:;");
			$(moreEl).on('click', displayTable(id, tdtype, moreObject, memberId));
		});
	}

	function initModernList() {
		$('#list-container > div.list-block > div > table > tbody[class=list-item] > tr.list-table-data > td.data.title').each(function () {
			if ($(this).attr('id') === 'list-extra') {
				return true;
			} else {
				$(this).attr('id', 'list-extra');
			}
			var id = $(this).find('> a').attr('href').match(/\/(\d+)\//)[1];
			var moreEl = $(this).find('> div > span.more > a');
			var memberId = $('body')[0].dataset.ownerId;
			var moreObject = $("#more-" + id);

			var el = $(moreEl)[0],
				elClone = el.cloneNode(true);
			el.parentNode.replaceChild(elClone, el);
			$(elClone).attr("href", "javascript:;");
			$(elClone).on('click', displayTable(id, 1, moreObject, memberId));
		});
	}

	function initGlobalScrollListener() {
		document.addEventListener("scroll", function scroll(event) {
			// temporarily remove scroll listener to prevent multiple events
			event.currentTarget.removeEventListener(event.type, scroll);

			initModernList();

			setTimeout(function () {
				initGlobalScrollListener();
			}, 1000);
		});
	}

	//--------------------------------//
	//       Get Data Functions       //
	//--------------------------------//

	function getAnimeInfo(animeid, table) {
		var dataMap = store.get(animeid + 'MAP');

		$.get('/' + mal.type[0] + '/' + animeid, function (data) {
			$(data).find('#content .leftside > div.spaceit_pad:nth-child(n) > span:first-child').each(function () {
				var item = this.textContent.replace(/:/g, "");
				if (isNaN(item) && this.nextSibling) {
                    if (item == "Score") {
                        dataMap[item] = this.parentNode.querySelector('.score-label').textContent;
                    } else {
                        var props = this.parentNode.querySelectorAll('[itemprop]');
                        if (props.length > 0) {
                            dataMap[item] = [...props].map(prop => prop.nextElementSibling.outerHTML).join(", ");
                        } else if (this.nextSibling.textContent.trim() === "") {
                            dataMap[item] = this.nextElementSibling.outerHTML;
                        } else {
                            dataMap[item] = this.nextSibling.textContent;
                        }
                    }
				}
			});
			$(data).find('#content .rightside > table [itemprop=description]').each(function () {
				dataMap.Synopsis = this.innerHTML;
			});
			dataMap.Image = $(data).find("#content .leftside a > img").attr('data-src');

			table.innerHTML = displayAnimeInfo(animeid);

			if (mal.modern) {
				$(table).find('a').each(function () {
					$(this).hover(function () {
						$(this).css("text-decoration", "underline");
					}, function () {
						$(this).css("text-decoration", "none");
					});
				});
			}
		});
	}

	function getDataFromOriginalMore(preData, animeid) {
		var dataMap = store.get(animeid + 'MAP');

		// Time Spent Watching
		var start = preData.indexOf('Time Spent Watching');
		var end = preData.indexOf('<small>(');
		dataMap.TimeSpentWatching = preData.substring(start + 21, end);
		start = end;
		end = preData.indexOf('per episode');
		var episodeTime = preData.substring(start + 8, end);
		dataMap.EpisodeTime = episodeTime;
	}

	//--------------------------------//
	//     Display Data Functions     //
	//--------------------------------//

	function displayTable(animeid, tdtype, moreObject, memberId) {
		return function () {
			var table = $(moreObject).find('.td' + tdtype + '.borderRBL')[0];

			if (table && moreObject.hasClass("extraLoaded")) {
				if (moreObject.hasClass("extraShowing")) {
					table.innerHTML = store.get(animeid + 'Original');
					moreObject.toggleClass("extraShowing originalShowing");
				} else if (moreObject.hasClass("originalShowing")) {
					moreObject.removeClass("originalShowing");
					moreObject.hide();
				} else {
					table.innerHTML = displayAnimeInfo(animeid);
					moreObject.addClass("extraShowing");
					moreObject.show();
				}
			} else {
				$.post("/includes/ajax-no-auth.inc.php?t=6", { color: tdtype, id: animeid, memId: memberId, type: mal.type[0] }, function (data) {
					if (mal.modern) {
						moreObject.find(".more-content").html(data.html);
					} else {
						moreObject.html(data.html);
					}
					moreObject.show();
					moreObject.addClass("extraLoaded extraShowing");

					var table = $(moreObject).find('.td' + tdtype + '.borderRBL')[0];

					var dataMap = {};
					store.set(animeid + 'MAP', dataMap);
					store.set(animeid + 'Original', table.innerHTML);
					if (table !== null && mal.type[0] != "manga") {
						getDataFromOriginalMore(table.innerHTML, animeid);
						table.innerHTML = "Fetching data";
						getAnimeInfo(animeid, table);
					} else if (table !== null) {
						table.innerHTML = "Fetching data";
						getAnimeInfo(animeid, table);
					}
				}, "json");
			}
		};
	}

	function getDataValue(animeid, key) {
		var dataMap = store.get(animeid + 'MAP');
		return dataMap[key] || "Unavailable";
	}

    function getDataValues(animeid, key1, key2) {
		var dataMap = store.get(animeid + 'MAP');
		return dataMap[key1] || dataMap[key2] || "Unavailable";
	}

	function calcTimeNeeded(episodeTime, totalEpisodes) {
		if (totalEpisodes.trim() == "0" || totalEpisodes == "Unknown") {
			return 'Unknown';
		} else if (episodeTime.indexOf("0 hours, 0 minutes, and 0 seconds") > -1) {
			return 'Unknown';
		} else if (totalEpisodes.trim() == "1") {
			return episodeTime;
		} else {
			var str = episodeTime.split(' ');

			var totalSeconds = parseInt(str[5]) * totalEpisodes + (parseInt(str[2]) * totalEpisodes * 60) + (parseInt(str[0]) * totalEpisodes * 60 * 60);
			var seconds = totalSeconds % 60;
			var totalMinutes = Math.floor(totalSeconds / 60);
			var minutes = totalMinutes % 60;
			var hours = Math.floor(totalMinutes / 60);

			return hours + " hours, " + minutes + " minutes and " + seconds + " seconds";
		}
	}

	function displayAnimeInfo(animeid) {
		var episodes = getDataValue(animeid, 'Episodes');
		var chapters = getDataValue(animeid, 'Chapters');
		var volumes = getDataValue(animeid, 'Volumes');
		var image = getDataValue(animeid, "Image");
		var genres = getDataValues(animeid, 'Genre', 'Genres');
		var status = getDataValue(animeid, 'Status');
		var broadcast = getDataValue(animeid, 'Broadcast');
		var score = getDataValue(animeid, 'Score');
		var rank = getDataValue(animeid, 'Ranked');
		var popularity = getDataValue(animeid, 'Popularity');
		var studio = getDataValues(animeid, 'Studio', 'Studios');
		var source = getDataValue(animeid, 'Source');
		var premiered = getDataValue(animeid, 'Premiered');
		var aired = getDataValue(animeid, 'Aired');
		var published = getDataValue(animeid, 'Published');
		var type = getDataValue(animeid, 'Type');
		var demographic = getDataValues(animeid, 'Demographic', 'Demographics');
		var themes = getDataValues(animeid, 'Theme', 'Themes');

		var synopsis = getDataValue(animeid, 'Synopsis').replace(/\(Source.*[\s\S]*/g, "").replace(/\[.*\]/g, "").replace(/(<br>|\s+)*$/, "");

		var timeSpentWatching;
		var timeNeeded;
		if (mal.type[0] == "anime") {
			timeSpentWatching = getDataValue(animeid, 'TimeSpentWatching').replace("tes,", "tes");
			timeNeeded = '0';
			if (timeSpentWatching.indexOf("0 hours, 0 minutes and 0 seconds") > -1) {
				timeNeeded = calcTimeNeeded(getDataValue(animeid, 'EpisodeTime'), episodes);
			}
		}

        return `
<style>
.container {  display: grid;
  grid-template-columns: auto 1fr 1fr;
  grid-template-rows: min-content 1fr min-content;
  gap: 5px;
  grid-auto-flow: row;
  grid-template-areas:
    "image details genres"
    "image synopsis synopsis"
    "image discuss time";
}

.image { grid-area: image; }
.details { grid-area: details; }
.genres { grid-area: genres; text-align: right; }
.synopsis { grid-area: synopsis; padding: 5px 0; }
.discuss { grid-area: discuss; }
.time { grid-area: time; text-align: right; }
</style>

<div class="container">
  <div class="image">
    <img src="${image}">
  </div>
  <div class="details">
    ${(mal.type[0] == "anime") ? ("<b>Source: <\/b>" + source + "<br>") : ("<b>Type: <\/b>" + type + "<br>")}
    ${(status.indexOf("Currently Airing") > -1) ? ("<b>Broadcast: <\/b>" + broadcast + "<br>") : ("")}
    ${(mal.type[0] == "anime") ? ("<b>Episodes: <\/b>" + episodes + "<br>") : ("<b>Volumes: <\/b>" + volumes + "<br><b>Chapters: <\/b>" + chapters + "<br>")}
    <b>Score: </b>${score}<br>
    ${(studio.indexOf("add some") == -1 && mal.type[0] != "manga") ? ("<b>Studio: <\/b>" + studio + "<br>") : ("")}
    ${(mal.type[0] == "anime") ? (premiered != "Unavailable" ? ("<b>Premiered: <\/b>" + premiered + "<br>") : ("")) : ("")}
    ${(mal.type[0] == "anime") ? ("<b>Aired: <\/b>" + aired + "<br>") : ("<b>Published: <\/b>" + published + "<br>")}
    ${(themes !== "Unavailable") ? ("<b>Themes: <\/b>" + themes + "<br>") : ("")}
    ${(demographic !== "Unavailable") ? ("<b>Demographic: <\/b>" + demographic + "<br>") : ("")}
  </div>
  <div class="genres">${genres}</div>
  <div class="synopsis">${synopsis}</div>
  <div class="discuss">
    <a href="/forum/?${mal.type[0]}id=${animeid}" target="_blank">
      <small>Discuss ${mal.type[0].charAt(0).toUpperCase() + mal.type[0].slice(1)}</small>
    </a>
  </div>
  <div class="time">
    ${(mal.type[0] == "anime") ? ("<small>" + (timeNeeded == '0' ? ("<b>Time Spent Watching: <\/b>" + timeSpentWatching) : ("<b>Time To Complete: <\/b>" + timeNeeded)) + "<\/small><\/td>") : ("")}
  </div>
</div>
`;
	}
})(jQuery);