Cleared Pixiv Artists

Keeps track of Pixiv artists' works and image responses and adds a random link to one of those pages.

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name           Cleared Pixiv Artists
// @namespace      https://greasyfork.org/scripts/3634
// @description    Keeps track of Pixiv artists' works and image responses and adds a random link to one of those pages.
// @include        http://danbooru.donmai.us/*
// @include        https://danbooru.donmai.us/*
// @include        http://hijiribe.donmai.us/*
// @include        http://sonohara.donmai.us/*
// @include        http://www.donmai.us/*
// @include        http://donmai.us/*
// @include        http://www.pixiv.net/*
// @include        https://www.pixiv.net/*
// @grant          GM_setValue
// @grant          GM_getValue
// @version        2017.06.08
// ==/UserScript==

var waitingDays = 365;//Days since a page was cleared before the Random link will include it again (when all artists are cleared)
var worksWeight = 3;//Number of chances to give each member_illust link of being selected for the random link (response links get 1)

//////////////////////////////////////////////////////////////////////////////////////

if( window == window.top )
	addLinks();

function addLinks()
{
	if( typeof(GM_getValue) != "undefined" && GM_getValue('a', 'b') )
	{
		getList = function(name){ try{ return JSON.parse( GM_getValue(name,"[]") ); }catch(e){ return []; } }
		setList = function(name,list) { GM_setValue(name,JSON.stringify(list)); }
	}
	else if( location.host.indexOf("donmai.us") >= 0 )
	{
		return;//GM is needed to maintain a single list across both Pixiv and Danbooru, so Danbooru is disabled when that isn't available
	}
	else
	{
		//Use local storage instead.
		getList = function(name)
		{
			var value = localStorage.getItem("cleared_pixiv."+name);
			if( value )
				try{ return JSON.parse(value); } catch(e){}
			return [];
		}
		setList = function(name,list) { localStorage.setItem("cleared_pixiv."+name, JSON.stringify(list)); }
	}
	
	//Add random link
	var randomLink;
	if( location.hostname.indexOf("donmai") >= 0 )
	{
		//Danbooru: Add to right end of subnavbar
		randomLink = document.evaluate("//header/nav/menu[not(contains(@class,'main'))]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		if( !randomLink )
			return;
		randomLink = randomLink.appendChild( document.createElement("li") ).appendChild( document.createElement("a") );
	}
	else
	{
		//Pixiv: Add to right of whatever
		randomLink = document.getElementsByTagName("header")[0];
		if( !randomLink )
			return;
		randomLink = randomLink.appendChild( document.createElement("a") );
		randomLink.style.cssFloat = "right";
		randomLink.style.margin = "3px";
	}
	randomLink.id = "random_pixiv_link";
	
	setRandomLink();
	
	if( location.href.indexOf("//www.pixiv.net/member.php?id=") > 0 )
	{
		//Member profile
		addListListener( document.evaluate("//div[@class='worksListOthers']//p/a[contains(@href,'member_illust.php?id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "worksList" );
		addListListener( document.evaluate("//div[@class='worksListOthers']//p/a[contains(@href,'response.php?mode=all&id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "responseList" );
	}
	else if( location.href.indexOf("//www.pixiv.net/member_illust.php?id=") > 0 )
	{
		//Member's works
		addListListener( document.evaluate("//div/h1/a[contains(@href,'member_illust.php?id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "worksList" );		
	}
	else if( location.href.indexOf("//www.pixiv.net/response.php?mode=all&id=") > 0 )
	{
		//Image responses
		var header = document.evaluate("//div[@class='search_top_result']/h2", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		if( !header )
			addListListener( null, "responseList" );//deleted user
		else
		{
			var div = document.createElement("div");
			var link = div.appendChild( document.createElement("a") );
			link.textContent = header.textContent;
			link.href = location.href;
			addListListener(link, "responseList");
			header.innerHTML = "";
			header.appendChild(div);
		}
	}
	else if( location.href.indexOf("donmai.us/artist") >= 0 )
	{
		//Danbooru artist
		var links = document.evaluate('//ul/ul/li/a[contains(@href,"//www.pixiv.net/member.php?id=")]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
		for( var i = 0; i < links.snapshotLength; i++ )
			addListListener( links.snapshotItem(i), "worksList" );
	}
}

function setRandomLink()
{
	var randomLink = document.getElementById("random_pixiv_link");
	if( !randomLink )
		return;
	
	var worksList = getList("worksList");
	var responseList = getList("responseList");
	var found = 0, i, pending = [];
	
	var responseWeight = (worksWeight < 0 ? -worksWeight : 1);
	worksWeight = (worksWeight > 0 ? worksWeight : 1);
	
	function addToList(maxDate)
	{
		for( var i = 0; i < worksList.length; i++ )
			if( worksList[i].date < maxDate )
			{
				if( location.href.indexOf( "//www.pixiv.net/member_illust.php?id="+worksList[i].id ) < 0 )
					for( j = 0; j < worksWeight; j++ )
						pending.push( location.protocol+"//www.pixiv.net/member_illust.php?id="+worksList[i].id );
				found++;
			}
		for( var i = 0; i < responseList.length; i++ )
			if( responseList[i].date < maxDate )
			{
				if( location.href.indexOf( "//www.pixiv.net/response.php?mode=all&id="+responseList[i].id ) < 0 )
					for( j = 0; j < responseWeight; j++ )
						pending.push( location.protocol+"//www.pixiv.net/response.php?mode=all&id="+responseList[i].id );
				found++;
			}
	}
	addToList(1);
	
	if( !found && waitingDays > 1 )
		addToList( new Date().getTime() - waitingDays*24*3600*1000 );
	
	randomLink.title = found+" / "+(worksList.length+responseList.length)+" links pending";
	
	if( found > 0 )
	{
		randomLink.textContent = "Random pixiv link ("+found+")";
		randomLink.style.textDecoration = "";
		if( pending.length > 0 )
			randomLink.href = pending[Math.floor(Math.random()*pending.length)];
		else
			randomLink.removeAttribute("href");
	}
	else
	{
		randomLink.textContent = "Random pixiv link";
		randomLink.style.textDecoration = "line-through";
		randomLink.removeAttribute("href");
	}
}

function addListListener(curLink, listName)
{
	var thisList = getList(listName);
	var userID = (curLink ? curLink : location).href.replace(/.*id=(\d+).*/,"$1");
	
	if( /^\d+$/.test(userID) && ( listName == "worksList" || listName == "responseList" ) )
	{
		if( !curLink )
		{
			for( var index = thisList.length - 1; index >= 0; index-- )
				if( thisList[index].id == userID )
				{
					thisList.splice( index, 1 );
					setList(listName,thisList);
					setRandomLink();
					alert("Removed: "+userID+" from "+listName);
				}
			return;
		}
	}
	else return;
	
	curLink.parentNode.appendChild( document.createTextNode(" ") );
	var statusLink = curLink.parentNode.appendChild( document.createElement("a") );// Add / Remove
	statusLink.style.cursor = "pointer";
	statusLink.onclick = "return false;";
	
	var clearLink = curLink.parentNode.appendChild( document.createElement("a") ); // Clear
	clearLink.style.cursor = "pointer";
	clearLink.onclick = "return false;";
	
	var found = false;
	
	for( var i = thisList.length; i-- > 0; )
		if( userID == thisList[i].id )
		{
			if( found )
			{
				//Remove older duplicates that somehow managed to sneak in.
				thisList.splice( i, 1 );
				setList(listName,thisList);
			}
			else
			{
				if( thisList[i].date == 0 )
					clearLink.textContent = "/[Clear]";
				else
				{
					var days = Math.round( (new Date().getTime() - thisList[i].date)/(24*3600*1000) );
					clearLink.textContent = "/[Cleared "+days+" day"+(days != 1 ? "s" : "")+" ago]";
				}
				statusLink.textContent = "[Remove]";
				found = true;
			}
		}
	if( !found )
		statusLink.textContent = "[Add]";

	//Switch between "Add" and "Remove", modifying the list accordingly.
	statusLink.addEventListener("click", function()
	{
		var list = getList(listName);//Reload list in case it is being modified across multiple tabs
		for( var index = list.length - 1; index >= 0 && list[index].id != userID; index-- );
		
		if( !found )
		{
			//Add to list if it isn't there already.
			if( index < 0 )
				list.push( { id: userID, date: 0 } );
			found = true;
			
			statusLink.textContent = "[Remove]";
			clearLink.textContent = "/[Clear]";
		}
		else
		{
			//Remove from list if it's really there
			if( index >= 0 )
				list.splice( index, 1 );
			found = false;
			
			curLink.textContent = curLink.textContent.replace(/ \[.*/g,'');
			statusLink.textContent = "[Add]";
			clearLink.textContent = "";
		}
		setList(listName,list);
		setRandomLink();
	}, true );
	
	//Set link as cleared today
	clearLink.addEventListener("click", function()
	{
		var list = getList(listName);
		for( var index = 0; index < list.length && list[index].id != userID; index++ );
		list[index].id = userID;
		list[index].date = new Date().getTime();
		setList(listName,list);
		
		statusLink.textContent = "[Remove]";
		clearLink.textContent = "/[Cleared 0 days ago]";
		setRandomLink();
	}, true );
}

//document.body.innerHTML = "<b>Cleared Pixiv Artists</b><br><b>worksList</b> = "+JSON.stringify(getList("worksList"))+"<br><b>responseList</b> = "+JSON.stringify(getList("responseList"));