// ==UserScript== // @name Aniscripts // @namespace http://tampermonkey.net/ // @version 0.87 // @description Change stuff on Anilist.co // @author hoh // @match https://anilist.co/* // @grant none // @downloadURL none // ==/UserScript== (function(){ var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = ` .hohTime{ position : static; float : right; margin-right : 20px; margin-top : 10px; margin-left: auto; } .hohUnread{ border-right : 8px; border-color: rgba(var(--color-blue)); border-right-style : solid; } .hohNotification{ margin-bottom : 10px; background : rgb(var(--color-foreground)); border-radius : 4px; justify-content: space-between; line-height: 0; min-height: 70px; } .hohNotification *{ line-height: 1.15; } .hohUserImageSmall{ display : inline-block; background-position : 50%; background-repeat : no-repeat; background-size : cover; position : absolute; } .hohUserImage{ height : 72px; width : 72px; display : inline-block; background-position : 50%; background-repeat : no-repeat; background-size : cover; position:absolute; } .hohMediaImage{ height : 70px; margin-right : 5px; } .hohMessageText{ position : absolute; margin-top : 30px; margin-left : 80px; max-width : 330px; } .hohMediaImageContainer{ vertical-align : bottom; margin-left : 400px; display : inline; position: relative; display: inline-block; min-height: 70px; } .hohMediaImageContainer > a{ line-height: 0!important; } span.hohMediaImageContainer{ line-height: 0!important; } .hohCommentsContainer{ margin-top: 5px; } .hohCommentsArea{ margin : 10px; display : none; padding-bottom : 2px; margin-top: 5px; width: 95%; } .hohComments{ float : right; display : none; margin-top: -30px; margin-right: 15px; cursor : pointer; margin-left: 600px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .hohCombined .hohComments{ display : none!important; } .hohQuickCom{ padding : 5px; background-color : rgb(var(--color-background)); margin-bottom : 5px; } .hohQuickComName{ margin-right : 15px; color : rgb(var(--color-blue)); } .hohQuickComName::after{ content : ":"; } .hohQuickComContent{ margin-right: 40px; } .hohQuickComLikes{ float : right; display: inline-block; } .hohSpoiler::before{ color : rgb(var(--color-blue)); cursor : pointer; background : rgb(var(--color-background)); border-radius : 3px; content : "Spoiler, click to view"; font-size : 1.3rem; padding : 0 5px; } .hohSpoiler.hohClicked::before{ display : none; } .hohSpoiler > span{ display : none; } .hohMessageText > span > div.time{ display : none; } .hohUnhandledSpecial > div{ margin-top : -20px; } .hohMonospace{ font-family: monospace; } .hohSocialTabActivityCompressedContainer{ min-width:480px; } .hohSocialTabActivityCompressedStatus{ vertical-align: middle; padding-bottom: 7px; } .hohSocialTabActivityCompressedName{ vertical-align: middle; margin-left: 3px; } .hohForumHider{ margin-right: 3px; cursor: pointer; font-family: monospace; } .hohForumHider:hover{ color: rgb(var(--color-blue)); } .hohBackgroundCover{ height: 70px; width: 50px; display: inline-block; background-position: 50%; background-repeat: no-repeat; background-size: cover; margin-top: 1px; margin-bottom: 1px; } #hohDescription{ width: 280px; height: 150px; float: left; color: rgb(var(--color-blue)); } .hohBackgroundUserCover{ height: 70px; width: 70px; display: inline-block; background-position: 50%; background-repeat: no-repeat; background-size: cover; margin-top: 1px; margin-bottom: 1px; }; `; document.getElementsByTagName('head')[0].appendChild(style); document.APIcallsUsed = 0; var pending = {}; var APIcounter = setTimeout(function(){ document.APIcallsUsed = 0; },60*1000);//keep track of approximately how many more api calls we can use function lsTest(){//localStorage is great for not having to fetch the api data every time var test = "test"; try{ localStorage.setItem(test,test); localStorage.removeItem(test); return true; }catch(e) { return false; } } if(lsTest() === true){ var localStorageAvailable = true; var aniscriptsUsed = localStorage.getItem("aniscriptsUsed"); if(aniscriptsUsed === null){ aniscriptsUsed = { keys : [] }; } else{ aniscriptsUsed = JSON.parse(aniscriptsUsed); }; localStorage.setItem("aniscriptsUsed",JSON.stringify(aniscriptsUsed)); } else{ var localStorageAvailable = false; }; try{//looks at the nav var whoAmI = document.getElementById("nav").children[0].children[1].children[1].href.match(/[a-zA-Z0-9-]*\/$/)[0].slice(0,-1); } catch(err){ var whoAmI = ""; };//use later for some scripts Element.prototype.remove = function(){//more comfy way to remove DOM elements this.parentElement.removeChild(this); } NodeList.prototype.remove = HTMLCollection.prototype.remove = function() { for(var i = this.length - 1; i >= 0; i--) { if(this[i] && this[i].parentElement) { this[i].parentElement.removeChild(this[i]); } } } var badMarkupParser = function(string){//attempt to render some elements, like images and spoilers string = string.replace(/\n/g,"
"); var imageRegexp = /img[0-9]*\((.*?)\)/; var imageMatches = imageRegexp.exec(string); while(imageMatches){ string = string.replace(imageRegexp,""); imageMatches = imageRegexp.exec(string); }; var youEx = /youtube\((https?\:\/\/www\.youtube\.com\/watch\?v\=(.+?))\)/;//catch subgroup with url and one with the id var videoMatches = youEx.exec(string); while(videoMatches){ string = string.replace(youEx,"youtube " + videoMatches[2] + ""); /* visible: youtube(https://www.youtube.com/watch?v=MzEinM660h4) becomes youtube MzEinM660h4 as a clickable link */ videoMatches = youEx.exec(string); }; var matchPings = /\@([a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]+)/;//at least three characters in user names var pingMatches = matchPings.exec(string); while(pingMatches){ if(whoAmI.toUpperCase() === pingMatches[1].toUpperCase()){ string = string.replace( matchPings, "@" + pingMatches[1] + "" ); } else{ string = string.replace( matchPings, "@" + pingMatches[1] + "" ); }; pingMatches = matchPings.exec(string); }; var matchLinks = /\[(.+?)\]\((.+?)\)/; var linkMatches = matchLinks.exec(string); while(linkMatches){ string = string.replace(matchLinks,"" + linkMatches[1] + ""); linkMatches = matchLinks.exec(string); }; var matchBold = /__(.+?)__/; var boldMatches = matchBold.exec(string); while(boldMatches){ string = string.replace(matchBold,"" + boldMatches[1] + ""); boldMatches = matchBold.exec(string); }; //https://anilist.co/manga/98889/Maikosan-Chi-no-Makanaisan/ var matchMediaLinks = /https?\:\/\/anilist.co\/(anime|manga)\/(\d+?)\/(.*?)\/(\ |\)/; var mediaLinkMatches = matchMediaLinks.exec(string); while(mediaLinkMatches){ string = string.replace( matchMediaLinks, "[" + mediaLinkMatches[1] + "/" + mediaLinkMatches[3] + "]" + mediaLinkMatches[4] ); mediaLinkMatches = matchMediaLinks.exec(string); }; var matchSpoiler = /\~\!(.*?)\!\~/; var spoilerMatches = matchSpoiler.exec(string); while(spoilerMatches){ string = string.replace( matchSpoiler, "" + spoilerMatches[1] + "" ); spoilerMatches = matchSpoiler.exec(string); }; return string; }; var activityCache = {};//reduce API calls even if localStorage is not available. var handleResponse = function(response){ return response.json().then(function(json){ return response.ok ? json : Promise.reject(json); }); }; var handleError = function(error){ //alert("Error, check console"); //fixme console.error(error); }; var url = 'https://graphql.anilist.co';//Current Anilist API location var listActivityCall = function(query,variables,callback,vars,cache){ /* query=graphql request vars=just values to pass on to the callback function cache::true use cached data if available cache::false allways fetch new data */ var handleData = function(data){ pending[variables.id] = false; if(localStorageAvailable){ localStorage.setItem(variables.id + "",JSON.stringify(data)); aniscriptsUsed.keys.push(variables.id); if(aniscriptsUsed.keys.length > 1000){//don't hog to much of localStorage for(var i=0;i<10;i++){ localStorage.removeItem(aniscriptsUsed.keys[0]); aniscriptsUsed.keys.shift(); }; }; localStorage.setItem("aniscriptsUsed",JSON.stringify(aniscriptsUsed)); } else{ activityCache[variables.id] = data; }; callback(data,vars); }; var options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ query: query, variables: variables }) }; if(localStorageAvailable && cache){ var localStorageItem = localStorage.getItem(variables.id + ""); if(!(localStorageItem === null)){ callback(JSON.parse(localStorageItem),vars); console.log("localStorage cache hit"); return; }; } else if(activityCache.hasOwnProperty(variables.id) && cache){ callback(activityCache[variables.id],vars); console.log("cache hit"); return; }; fetch(url,options).then(handleResponse).then(handleData).catch(handleError); ++document.APIcallsUsed; console.log("fetching new data"); }; var generalAPIcall = function(query,variables,callback){ var handleData = function(data){ callback(data); }; var options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ query: query, variables: variables }) }; fetch(url,options).then(handleResponse).then(handleData).catch(handleError); ++document.APIcallsUsed; console.log("fetching new data"); }; var enhanceSocialTab = function(){ var perform = function(){ if(!document.URL.match(/https:\/\/anilist\.co\/(anime|manga)\/\d*\/[0-9a-zA-Z-]*\/social/)){ return; }; var listOfActs = document.getElementsByClassName("activity-entry"); for(var i=0;i 1) ){ listOfActs[i].marked = true; listOfActs[i].children[0].children[0].children[0].remove();//remove cover image var elements = listOfActs[i].children[0].children[0].children[0].children; elements[2].parentNode.insertBefore(elements[2],elements[0]);//move profile picture to the beginning of the line elements[0].parentNode.parentNode.style.minHeight = "70px"; elements[0].parentNode.classList.add("hohSocialTabActivityCompressedContainer"); elements[0].style.verticalAlign = "bottom"; elements[0].style.marginTop = "0px"; elements[1].classList.add("hohSocialTabActivityCompressedName"); elements[2].classList.add("hohSocialTabActivityCompressedStatus"); listOfActs[i].style.marginBottom = "10px"; }; }; /*add average score to social tab*/ var listOfFollowers = document.getElementsByClassName("follow"); var averageScore = 0; var averageCount = 0; for(var i=0;i 1){ this.parentNode.parentNode.parentNode.children[1].style.display = "none"; }; } else{ this.innerHTML = "[-]"; this.parentNode.parentNode.children[1].style.display = "block"; if(this.parentNode.parentNode.parentNode.children.length > 1){ this.parentNode.parentNode.parentNode.children[1].style.display = "block"; }; }; }; comments[i].children[0].children[0].insertBefore(hider,comments[i].children[0].children[0].children[0]); }; //comments[i].remove(); }; }; var tryAgain = function(){//loop the notification script until we leave that page setTimeout(function(){ perform(); if(document.URL.match(/https:\/\/anilist\.co\/forum\/thread\/.*/)){ tryAgain() } else{ activeScripts.forumComments = false; } },100); }; activeScripts.forumComments = true; perform(); tryAgain(); }; var enhanceStaff = function(){ var perform = function(){ if(!document.URL.match(/https:\/\/anilist\.co\/staff\/.*/)){ return; }; var roleCards = document.getElementsByClassName("role-card"); for(var i=0;i 100){//horrible test, but we have not markup to go from bestGuess = i; }; }; if(bestGuess == false){ return; }; if(possibleTagContainers[bestGuess].hasOwnProperty("hohMarked")){ return; } else{ possibleTagContainers[bestGuess].hohMarked = true; }; var superBody = document.getElementsByClassName("el-dialog__body")[0]; var descriptionTarget = document.createElement("span"); descriptionTarget.id = "hohDescription"; superBody.insertBefore(descriptionTarget,superBody.children[2]); for(var i=0;i 1 && notifications[i].classList[1] != "hasMedia" ){ //"notification unread" classlist active.unread = true; } else{ active.unread = false; }; active.type = "special"; //by default every activity is some weird thing we are displaying as-is active.link = "aaa";//fixme if(//check if we can query that notifications[i].children.length >= 1 && notifications[i].children[1].children.length && notifications[i].children[1].children[0].children.length && notifications[i].children[1].children[0].children[0].children.length ){ // active.directLink = notifications[i].children[1].children[0].children[0].href active.text = notifications[i].children[1].children[0].children[0].innerHTML; active.textName = notifications[i].children[1].children[0].children[0].childNodes[0].textContent; active.textSpan = notifications[i].children[1].children[0].children[0].childNodes[1].textContent; active.link = notifications[i].children[1].children[0].children[0].href.match(/[0-9]+/)[0]; var testType = notifications[i].children[1].children[0].children[0].children[0].textContent; if(testType == " liked your activity."){ active.type = "likeActivity"; } else if(testType == " replied to your activity."){ active.type = "replyActivity"; } else if(testType == " sent you a message."){ active.type = "messageActivity"; } else if(testType == " liked your activity reply."){ active.type = "likeReplyActivity"; } else if(testType == " mentioned you in their activity."){ active.type = "mentionActivity"; } // }; if(active.type == "special"){ if( notifications[i].children.length >= 1 && notifications[i].children[1].children.length && notifications[i].children[1].children[0].children.length >= 2 && notifications[i].children[1].children[0].children[1].textContent == " started following you." ){ active.type = "followActivity"; active.directLink = notifications[i].children[1].children[0].children[0].href active.text = notifications[i].children[1].children[0].children[0].innerHTML; active.textName = notifications[i].children[1].children[0].children[0].textContent; active.textSpan = notifications[i].children[1].children[0].children[1].textContent; } else if( notifications[i].children.length >= 1 && notifications[i].children[1].children.length && notifications[i].children[1].children[0].children.length >= 4 && notifications[i].children[1].children[0].children[3].textContent == " aired." ){ active.type = "airingActivity"; active.directLink = notifications[i].children[1].children[0].children[0].href active.text = notifications[i].children[1].children[0].innerHTML; } else{ active.text = notifications[i].children[1].innerHTML; }; }; if( notifications[i].children.length > 1 && notifications[i].children[1].children.length > 1 ){ active.time = notifications[i].children[1].children[1].innerHTML; } else{ active.time = document.createElement("span"); }; active.image = notifications[i].children[0].style.backgroundImage; active.href = notifications[i].children[0].href; activities.push(active); }; if(activities.length == prevLength){ if(retries == 0){ return 0; } else{ retries--; }; } else{ prevLength = activities.length; retries = 3; }; if(document.getElementById("hohNotifications")){ document.getElementById("hohNotifications").remove(); }; var newContainer = document.createElement("div"); newContainer.id = "hohNotifications"; var notificationsContainer = document.getElementsByClassName("notifications"); if(!notificationsContainer.length){ return; } else{ notificationsContainer = notificationsContainer[0]; }; notificationsContainer.insertBefore(newContainer,notificationsContainer.firstChild); for(var i=0;i 5){ timeHideFlag = true; }; if(counter == 1){ while( i + counter < activities.length && activities[i + counter].type == "likeActivity" && activities[i + counter].link == activities[i].link ){//several people likes one of your activities var miniImageWidth = 40; var miniImage = document.createElement("a"); miniImage.classList.add("hohUserImageSmall"); miniImage.href = activities[i + counter].href; miniImage.style.backgroundImage = activities[i + counter].image; miniImage.style.height = miniImageWidth + "px"; miniImage.style.width = miniImageWidth + "px"; miniImage.style.marginLeft = (72 + (counter-1)*miniImageWidth) + "px"; newNotification.appendChild(miniImage); counter++; }; if(counter > 1){ text.style.marginTop = "45px"; activities[i].textName += " +"; }; } else{ newNotification.classList.add("hohCombined"); }; text.href = activities[i].directLink; var textName = document.createElement("span"); var textSpan = document.createElement("span"); textName.innerHTML = activities[i].textName; textSpan.innerHTML = activities[i].textSpan; textName.style.color = "rgb(var(--color-blue))"; text.appendChild(textName); if(activityCounter > 1){ textSpan.innerHTML = " liked your activities."; }; text.appendChild(textSpan); i += counter -1; } else if(activities[i].type == "replyActivity"){ var notNotImage = document.createElement("img"); notNotImage.classList.add("hohMediaImage"); notNotImage.classList.add(activities[i].link); notNotImageContainer.appendChild(notNotImage); var counter = 1; while( i + counter < activities.length && activities[i + counter].type == "replyActivity" && activities[i + counter].link == activities[i].link ){ var miniImageWidth = 40; var miniImage = document.createElement("a"); miniImage.classList.add("hohUserImageSmall"); miniImage.href = activities[i + counter].href; miniImage.style.backgroundImage = activities[i + counter].image; miniImage.style.height = miniImageWidth + "px"; miniImage.style.width = miniImageWidth + "px"; miniImage.style.marginLeft = (72 + (counter-1)*miniImageWidth) + "px"; newNotification.appendChild(miniImage); counter++; }; if(counter > 1){ text.style.marginTop = "45px"; activities[i].textName += " +"; }; text.href = activities[i].directLink; var textName = document.createElement("span"); var textSpan = document.createElement("span"); textName.innerHTML = activities[i].textName; textSpan.innerHTML = activities[i].textSpan; textName.style.color = "rgb(var(--color-blue))"; text.appendChild(textName); text.appendChild(textSpan); i += counter -1; } else if( activities[i].type == "messageActivity" || activities[i].type == "likeReplyActivity" || activities[i].type == "mentionActivity" ){ var notNotImage = document.createElement("img"); notNotImage.classList.add("hohMediaImage"); notNotImage.classList.add(activities[i].link); notNotImageContainer.appendChild(notNotImage); text.href = activities[i].directLink; var textName = document.createElement("span"); var textSpan = document.createElement("span"); textName.innerHTML = activities[i].textName; textSpan.innerHTML = activities[i].textSpan; textName.style.color = "rgb(var(--color-blue))"; text.appendChild(textName); text.appendChild(textSpan); } else if(activities[i].type == "airingActivity"){ //text.href = activities[i].directLink; var textSpan = document.createElement("span"); textSpan.innerHTML = activities[i].text; text.appendChild(textSpan); } else if(activities[i].type == "followActivity"){ text.href = activities[i].directLink; var textName = document.createElement("span"); var textSpan = document.createElement("span"); textName.innerHTML = activities[i].textName; textSpan.innerHTML = activities[i].textSpan; textName.style.color = "rgb(var(--color-blue))"; text.appendChild(textName); text.appendChild(textSpan); } else{//display as-is var textSpan = document.createElement("span"); textSpan.classList.add("hohUnhandledSpecial"); textSpan.innerHTML = activities[i].text; text.appendChild(textSpan); }; var time = document.createElement("div"); time.classList.add("hohTime"); time.innerHTML = activities[i].time; var commentsContainer = document.createElement("div"); commentsContainer.classList.add("hohCommentsContainer"); commentsContainer.classList.add("b" + activities[i].link);//possible replies var comments = document.createElement("a"); comments.classList.add("hohComments"); comments.innerHTML = "comments+"; comments.onclick = function(){ if(this.innerText == "comments+"){ this.innerHTML = "comments-"; this.parentNode.children[1].style.display = "inline-block"; var query = "query ($id: Int!) { Activity(id: $id) { ... on TextActivity { id userId type replyCount text createdAt user { id name avatar { large } } likes { id name avatar { large } } replies { id text createdAt user { id name avatar { large } } likes { id name avatar { large } } } } ... on ListActivity { id userId type status progress replyCount createdAt user { id name avatar { large } } media { coverImage { large } id title { userPreferred } } likes { id name avatar { large } } replies { id text createdAt user { id name avatar { large } } likes { id name avatar { large } } } } ... on MessageActivity { id type replyCount createdAt messenger { id name avatar { large } } likes { id name avatar { large } } replies { id text createdAt user { id name avatar { large } } likes { id name avatar { large } } } } } }"; var variables = { id: +this.parentNode.classList[1].substring(1) }; var vars = {}; var commentCallback = function(data,vars){ var listOfComments = document.getElementsByClassName("b" + data.data.Activity.id); for(var k=0;k 1){ quickComLikes.innerHTML = data.data.Activity.replies[l].likes.length + "♥"; } else if(data.data.Activity.replies[l].likes.length){ quickComLikes.innerHTML = "♥"; }; for(var m=0;m+"; this.parentNode.children[1].style.display = "none"; }; }; comments.classList.add("link"); var commentsArea = document.createElement("div"); commentsArea.classList.add("hohCommentsArea"); commentsContainer.appendChild(comments); commentsContainer.appendChild(commentsArea); newNotification.appendChild(notImage); newNotification.appendChild(text); newNotification.appendChild(notNotImageContainer); if(!timeHideFlag){ newNotification.appendChild(time); }; newNotification.appendChild(commentsContainer); newContainer.appendChild(newNotification); }; for(var i=0;document.APIcallsUsed < 90;i++){//heavy if(!activities.length || i >= activities.length){//loading is difficult to predict. There may be nothing there when this runs break; }; var imageCallBack = function(data,vars){ var type = data.data.Activity.type; var extra = 0; for(var j=0;j 1){ quickComLikes.innerHTML = data.data.Activity.replies[l].likes.length + "♥"; } else if(data.data.Activity.replies[l].likes.length){ quickComLikes.innerHTML = "♥"; }; for(var m=0;m 1 ){ matched = true; if(favSection[0].children[listLocation].children[1].children.length == 25){ var addMoreFavs = function(data){ if(data.data.User.favourites.anime.edges.length == 0){//user only has exactly 25 favs return; }; for(var i=0;i