Deviant Statistics: Fully Conclusive (obsolete)

By BoffinbraiN Last update Oct 1, 2008 — Installed 5,524 times.

There are 2 previous versions of this script.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name           Deviant Statistics: Fully Conclusive
// @namespace      http://boffinbrain.deviantart.com/
// @description    Compiles a comprehensive list of useful statistics about users and displays them on deviants' userpages.
// @include        http://*.deviantart.com/
// @include        http://*.deviantart.com/?offset=*
// ==/UserScript==




// Startup

var profile_box = document.getElementById("deviant");
var info_box = document.getElementById("deviant-info");
var stats_box = document.getElementById("deviant-stats");
if (!profile_box || !info_box || !stats_box) return;

var version = 1.4;
var deviant_name = window.location.hostname.substring(0, window.location.hostname.indexOf("."));
var stats_url = "http://"+deviant_name+".deviantart.com/stats/gallery/";
var username = "_"; try {username = unsafeWindow.deviantART.deviant.username;} catch(e) {}
var form_prefix = "devstats-";
var G, U;
var debug = false;
var retry_count = 0;
var loading_box;

function main()
{
	GM_addStyle([
		"#deviant div.stats-pager {margin-bottom:8px;}",
		"#deviant div.stats-pager a.active {color:inherit; text-decoration:none !important; font-weight:bold; letter-spacing:-0.05em;}",
		"#deviant #deviant-stats {text-align:right !important;}"
	].join("\n"));

	loading_box = document.createElement("div");
	loading_box.style.marginBottom = "8px";
	info_box.insertBefore(loading_box, info_box.childNodes[0]);

	// If user is banned, the javascript file redirects to a notification page.
	try
	{
		var symbol = profile_box.getElementsByTagName("div")[0].getElementsByTagName("h1")[0].innerHTML.substring(0,1);
		if (symbol=="!")
		{
			status("User is banned.  No statistics can be fetched.");
			return;
		}
	}
	catch(e) {}

	// Auto-load, or add a link to start
	if (GM_getValue("autoload", 0))
	{
		getStats();
	}
	else
	{
		var load_link = hyperlink("javascript:void(0);", "Load Statistics");
		load_link.addEventListener("click", getStats, false);
		status(load_link);
	}
}




// Functions

function getStats()
{
	status("Loading statistics...");
	GM_xmlhttpRequest
	({
		method: "GET",
		url: stats_url,
		onload: displayStats,
		onerror: displayError
	});
}

function displayError(statuscode)
{
	var message = "Couldn't get statistics"+(statuscode!=null&&statuscode!=200?" (error "+statuscode+") ":"")+". ";
	if (statuscode==403)
	{
		message = "User has blocked access to their gallery statistics.  How sad.";
	}
	else if (retry_count<4)
	{
		message += "Trying again... ("+(++retry_count)+")";
		getStats();
	}
	else
	{
		message += "Given up after "+retry_count+" attempts.";
	}
	status(message);
}

function displayStats(responseDetails)
{
	// Hopefully, there weren't any errors
	if (responseDetails.status!=200)
	{
		displayError(responseDetails.status);
		return;
	}

	var timer = [new Date()];

	if (!processStats(responseDetails.responseText))
	{
		status("There was a problem processing the statistics.");
		return false;
	}

	timer.push(new Date());

	status("");

	// Compile a list of useful extra links for later
	var track_links = [];
	var track_link;
	var track_divs = stats_box.getElementsByTagName("div");
	for(var i=track_divs.length-1; i>=0; i--)
	{
		var track_div_links = track_divs[i].getElementsByTagName("a");
		for(var j=0; j<track_div_links.length; j++) track_links.push(track_div_links[j]);
		stats_box.removeChild(track_divs[i]);
	}
	if (U.subscribed && U.journalexists) track_links.push(hyperlink("http://backend.deviantart.com/rss/journal/"+U.username+".xml", "Journal RSS feed"));
	track_links.push(hyperlink("http://backend.deviantart.com/rss.xml?q=gallery%3A"+U.username+"+sort%3Atime&type=deviation", "Gallery RSS feed"));

	// Make presumptuous assumptions about the user
	var hypotheses = makeHypotheses(G);

	// Make pages and tabs on the left and right hand sides

	var pager_left = new Pager(info_box);
	var pager_right = new Pager(stats_box);
	
	pager_left.importPage(info_box.getElementsByTagName("ul")[0], "Profile");
	pager_right.importPage(stats_box.getElementsByTagName("ul")[0], "Presence");

	var pages_left = {"Deviations":[], "Files":[], "Averages":[], "Options":[]};
	var pages_right = {"Community":[], "Hypotheses":[], "Tracking":[]};
	
	// Populate pages with information

	if (U.deviations>0)
	{
		pages_left["Deviations"] = 
		[
			addDeviation("Most comments:",     G[U.top_comments],   number(G[U.top_comments].comments),     G[U.top_comments].comments>0),
			addDeviation("Most favourites:",   G[U.top_favourites], number(G[U.top_favourites].favourites), G[U.top_favourites].favourites>0),
			addDeviation("Most views:",        G[U.top_views],      number(G[U.top_views].views)),
			addStat("Favourite category:",     number(U.top_majorcat_num),   "http://"+U.username.toLowerCase()+".deviantart.com/gallery/"+U.top_majorcat_name+"/", U.top_majorcat_name),
			addStat("Favourite sub-category:", number(U.top_cat_num),        "http://"+U.username.toLowerCase()+".deviantart.com/gallery/"+U.top_cat_name+"/",      U.top_cat_name.split('/').reverse()[0]),
			addStat("Total deviation views:", number(U.total_views))
		];
		pages_left["Files"] =
		[
			addDeviation("Most downloads:",     G[U.top_downloads],  number(G[U.top_downloads].fullviews), G[U.top_downloads].fullviews>0),
			addDeviation("Largest resolution:", G[U.top_resolution], G[U.top_resolution].resolution,       G[U.top_resolution].area>640*480,     U.username+" doesn't have any large images."),
			addDeviation("Largest file:",       G[U.top_filesize],   G[U.top_filesize].size,               G[U.top_filesize].filesize>1024*1024, U.username+" doesn't have any files over 1MB.")
		];
		pages_left["Averages"] =
		[
			addStat("Favourites received per day:", number(U.total_favourites/(U.age+1), 1)),
			addStat("Comments per deviation:",      number(U.total_comments/U.deviations, 1)),
			addStat("Favourites per deviation:",    number(U.total_favourites/U.deviations, 1)),
			addStat("Deviation frequency"+(U.scraps?" (with scraps):":""), number((U.age+1)/(U.deviations+U.scraps), 1)+" days")
		];
	}
	pages_left["Averages"].push(addStat("Pageviews per day:", number(U.pageviews/(U.age+1), 1)));

	for(var i=0; i<hypotheses.length && i<7; i++) pages_right["Hypotheses"].push(addStat(hypotheses[i]));
	for(var i=0; i<track_links.length; i++) pages_right["Tracking"].push(track_links[i]);
	pages_right["Community"] = 
	[
		addReversedStat("Friend"+s(U.friends),                         number(U.friends)),
		addReversedStat("Watcher"+s(U.friendswatching),                number(U.friendswatching)),
		addReversedStat("Comment"+s(U.comments)+" Given",              number(U.comments)),
		addReversedStat("Comment"+s(U.total_comments)+" Received",     number(U.total_comments)),
		addReversedStat("Favourite"+s(U.favourites)+" Given",          number(U.favourites)),
		addReversedStat("Favourite"+s(U.total_favourites)+" Received", number(U.total_favourites))
	];
	if (U.favourites) pages_right["Community"].push(addReversedStat("Comments Given Per Favourite", number(U.commentsdeviations/(U.favourites+1), 1)));


	// Options Page
	var update_img = document.createElement("img");
	update_img.alt = "Version "+version+". Go to the script homepage to check for updates";
	update_img.title = "Version "+version+". Go to the script homepage to check for updates";
	update_img.src = "http://boffinbrain.nfshost.com/projects/deviantart/deviantstatistics/"+version+"/"+username+"/"+U.username+"/";
	
	var update_link = hyperlink("http://www.deviantart.com/deviation/81516142/");
	update_link.appendChild(update_img);

	pages_left["Options"] =
	[
		update_link,
		addCheckBox("autoload", "Get statistics automatically on page load"),
		addCheckBox("hover", "Switch tabs when I move my mouse over them")
	];

	timer.push(new Date());
	if (debug)
	{
		for(var i=timer.length-1; i>0; i--) timer[i] = timer[i]-timer[i-1] + "ms";
		timer.splice(0,1);
		pages_left["Options"].push(addStat(null, null, stats_url, "Performance figures: "+timer.join(", ")));
	}
	
	// Display the pages
	
	for(var p in pages_left)
	{
		var page = pages_left[p];
		if (page.length>0)
		{
			var new_page = pager_left.addPage(p);
			for(var i=0; i<page.length; i++) pager_left.addElem(new_page, page[i]);
		}
	}

	for(var p in pages_right)
	{
		var page = pages_right[p];
		if (page.length>0)
		{
			var new_page = pager_right.addPage(p);
			for(var i=0; i<page.length; i++) pager_right.addElem(new_page, page[i]);
		}
	}
	
	// I'm done with the data
	delete G;
}

function makeHypotheses()
{
	var h = [];
	
	with(U)
	{
		var avg = deviations>0 ? (total_comments+total_favourites)/deviations : 0;
		var plz = username.length>3 && username.toLowerCase().lastIndexOf("plz")>=username.length-4;

		if (age<=7) h[h.length] = "New deviant!";
		else if (age>365*5) h[h.length] = "Old-school deviant!";
		if (offlinetime==-1) h[h.length] = "Has never logged in!";
		if (deviations>1)
		{
			if (avg<10 && friendswatching<50)        h[h.length] = "Unknown artist";
			else if (avg<30 && friendswatching<200)  h[h.length] = "Emerging artist";
			else if (avg<100 && friendswatching<500) h[h.length] = "Established artist";
			else if (avg<300)                        h[h.length] = "Popular artist";
			else if (avg<1000)                       h[h.length] = "Very popular artist";
			else                                     h[h.length] = "EXTREMELY popular artist";
			if (avg>150 && friendswatching>1000) h[h.length] = "Has a massive fan-base";
			if (deviations>10 && avg>50 && top_majorcat_name=="anime") h[h.length] = "OMG Anime artist!";
			if (deviations>20 && avg<10 && friendswatching<50)
			{
				if (age<120)              h[h.length] = "The world has yet to see your art";
				else if (pageviews>10000) h[h.length] = "People seem to pass you by...";
			}
		}
		if (age>90 && !plz)
		{
			if (age>360 && offlinetime>300)
			{
				if (pageviews>1000) h[h.length] = "Rest in peace, abandoned account";
				else                h[h.length] = "A terrible waste of a username";
			}
			if (deviations==1 && total_favourites>500) h[h.length] = "One-hit wonder";
			if (friendswatching<=1 && friends<=2) h[h.length] = "Doesn't have any love :(";
			if (comments==0)
			{
				if (!journalexists && deviations==0 && favourites<=1 && offlinetime<30) h[h.length] = "A very boring deviant";
				else if (comments/(age+1)<0.15) h[h.length] = "Rarely comments on anything";
				else h[h.length] = "Has never commented on anything!";
			}
			if (!journalexists && deviations>0) h[h.length] = "Doesn't have much to write about.";
		}
		if (comments/(age+1)>20) h[h.length] = "Very busy deviant";
		if (deviations>5 && deviations/(age+1)>0.3) h[h.length] = "Very productive artist";
		if (deviations>30 && total_categories>=deviations/2.5) h[h.length] = "A multi-talented artist";
		else if (deviations>20 && total_categories<3) h[h.length] = "Has very specific tastes";
		if (commentsdeviations/(favourites+1)>20 && comments/(age+1)>3) h[h.length] = "Critic";
		if (forum_posts>2000 || forum_posts/(age+1)>4) h[h.length] = "Frequent Forumer";
		if (commentsnews>100 && commentsnews/(age+1)>1) h[h.length] = "Frequent news reader";
		if (deviations==0 && friendswatching>100) h[h.length] = "Why are people watching me?";
		if (plz) h[h.length] = "PLZ account!";
		if (deviations>300) h[h.length] = "Too many deviations!";
		if (comments/(favourites+1)<0.8 && favourites>1000) h[h.length] = "Fave-whore!";
		if (friends>1000) h[h.length] = "DevWatch-whore!";
		if (commentsdeviants>3000 || commentsdeviants/(age+1)>3.3) h[h.length] = "Obsessively shouts on people's userpages!";
	}
	
	return h;
}

function processStats(data)
{
	data = getTextRegion(data, "deviantART.pageData=", "\n</script>");
	if(data == '') return false;
	eval("G = "+data.replace(/\\'|\\\\"/g, "'")+";");
	
	U = G.userstats;
	G = G.devstats;

	// Extra stats that can be derived from given information and from userpage
	U.total_comments     = 0;	U.total_favourites   = 0;
	U.top_views          = 0;	U.top_downloads      = 0;
	U.top_favourites     = 0;	U.top_comments       = 0;
	U.top_filesize       = 0;	U.top_resolution     = 0;
	U.top_majorcat_num   = 0;	U.top_majorcat_name  = '';
	U.top_cat_num        = 0;	U.top_cat_name       = '';
	U.total_categories   = 0;	U.total_views        = 0;

	U.offlinetime        = -1;
	U.age           = Math.round((new Date() - Date.parse(U.since)) / (1000*3600*24));
	U.journalexists = xpath("//div[contains(@class,'journalcontrol')]").snapshotItem(0) ? true : false;

	var info_list = info_box.getElementsByTagName("ul")[0].getElementsByTagName("li");
	var list;
	for(var i=0; list=info_list[i]; i++)
	{
		var html = list.innerHTML;
		if (html.indexOf("Online")==0 || html.indexOf("Idle")==0 || html.indexOf("Invisible")==0)
		{
			// Assume user to be online
			U.offlinetime = 0;
			break;
		}
		else if (html.indexOf("Offline for")==0)
		{
			// Measure the offline time in days, to the nearest whole week.
			// We're not concerned with any greater level of accuracy.
			var offset = html.indexOf("w");
			if (offset>12) U.offlinetime = html.substring(12, offset)*7;
			else U.offlinetime = 0;
			break;
		}
		// If none of the above, then the user has never logged in
	}

	// If the user has no deviations then none of the following applies
	if (U.deviations==0) return true;

	var categories = [];
	var major_categories = [];
	var dev, size, dims;
	for(var i=0; dev=G[i]; i++)
	{
		// Area is width*height of deviation
		dims = dev.resolution.split("x");
		dev.area = dims[0]*dims[1];

		// Determine file size in bytes
		size = dev.size.split(' ');
		size[0] = parseFloat(size[0].replace(',',''));
		if      (size[1] == 'KB') size[0] *= 1024;
		else if (size[1] == 'MB') size[0] *= 1024 * 1024;
		dev.filesize = size[0];

		U.total_comments += dev.comments;
		U.total_favourites += dev.favourites;
		U.total_views += dev.views;

		if (dev.views      > G[U.top_views].views)           U.top_views = i;
		if (dev.fullviews  > G[U.top_downloads].fullviews)   U.top_downloads = i;
		if (dev.favourites > G[U.top_favourites].favourites) U.top_favourites = i;
		if (dev.comments   > G[U.top_comments].comments)     U.top_comments = i;
		if (dev.filesize   > G[U.top_filesize].filesize)     U.top_filesize = i;
		if (dev.area       > G[U.top_resolution].area)       U.top_resolution = i;

		// There is likely to be much confusion between the different usages of 'category' here.
		//  - 'Category' is the full category path, i.e. "Customization/Icons/Emoticons/Static"
		//  - 'Major Category' is the root category only, i.e. "Customization"

		dev.majorcategory = dev.category.split('/')[0];

		if (major_categories[dev.majorcategory]) major_categories[dev.majorcategory]++;
		else major_categories[dev.majorcategory] = 1;
		if (major_categories[dev.majorcategory] > U.top_majorcat_num)
		{
			U.top_majorcat_name = dev.majorcategory;
			U.top_majorcat_num = major_categories[dev.majorcategory];
		}

		if (categories[dev.category]) categories[dev.category]++;
		else
		{
			categories[dev.category] = 1;
			U.total_categories++;
		}
		if (categories[dev.category] > U.top_cat_num)
		{
			U.top_cat_name = dev.category;
			U.top_cat_num = categories[dev.category];
		}
	}

	return true;
}

function xpath(query)
{
	// Standard XPATH shorthand function
	return document.evaluate(query, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}

function getTextRegion(text, start, end)
{
	// Return the text between the first instance of 'start' and the next proceeding instance of 'end' in 'text'.
	if(text.indexOf(start)<0) return "";
	text = text.substring(text.indexOf(start)+start.length);
	if(text.indexOf(end)<0) return text;
	text = text.substring(0, text.indexOf(end));
	return text;
}

function status(text)
{
	// Show current working status of the script
	if (loading_box.childNodes[0]) loading_box.removeChild(loading_box.childNodes[0]);
	if (typeof text == "string")
	{
		if (text == '') return;
		loading_box.appendChild(document.createTextNode(text));
	}
	else
	{
		loading_box.appendChild(text);
	}
}

function element(tag, attributes, children)
{
	var e = document.createElement(tag);
	for(var a in attributes)
	{
		e.setAttribute(a, attributes[a]);
	}
	for(var c in children)
	{
		if (typeof children[c] == "string") e.appendChild(document.createTextNode(children[c]));
		else e.appendChild(children[c]);
	}
	return e;
}

function hyperlink(url, text)
{
	return element("a", {"class":"a", "href":url}, text);
}

function number(x, y)
{
	if (x==null) return "none";
	if (y!=null) x = x.toFixed(y);
	x += "";
	var components = x.split(".");
	var result = components[0];
	if (result.length>3)
	{
		var start = result.length-3;
		result = number(result.substring(0,start))+","+result.substring(start);
	}
	if (components[1]!=null) result += "."+components[1];
	return result;
}

function s(x)
{
	return x!=1 ? "s" : "";
}

function addDeviation(label, deviation, statistic, conditional, alternate)
{
	if (conditional==null || conditional==true) return addStat(label, statistic, "http://www.deviantart.com/deviation/"+deviation.id+"/", deviation.title);
	else return addStat(alternate);
}

function addStat(key, value, url, link)
{
	var item = document.createDocumentFragment();
	if (key) item.appendChild(document.createTextNode(key+(link||value?" ":"")));
	if (link && url) item.appendChild(hyperlink(url, link));
	if (value) item.appendChild(element("strong", null, link ? " ("+value+")" : value));
	return item;
}

function addReversedStat(key, value)
{
	var item = document.createDocumentFragment();
	if (value) item.appendChild(element("strong", null, value==0 ? "No " : value+" "));
	item.appendChild(document.createTextNode(key));
	return item;
}

function addCheckBox(setting, text)
{
	var checkbox = document.createElement("input");
	checkbox.type = "checkbox";
	checkbox.style.verticalAlign = "middle";
	checkbox.id = form_prefix+setting;
	checkbox.checked = GM_getValue(setting, 0);
	checkbox.addEventListener("change", function(){GM_setValue(setting, this.checked);}, false);

	var label = document.createElement("label");
	label.setAttribute("for", form_prefix+setting);
	label.appendChild(checkbox);
	label.appendChild(document.createTextNode(text));
	
	return label;
}




// Pager Class

Pager.prototype.container;
Pager.prototype.navigator;
Pager.prototype.pages;
Pager.prototype.tabs;
Pager.prototype.activeTab;

function Pager(container)
{
	this.container = container;
	this.navigator = document.createElement("div");
	this.navigator.className = "stats-pager";
	if (this.container.childNodes[0]) this.container.insertBefore(this.navigator, this.container.childNodes[0]);
	else                              this.container.appendChild(this.navigator);
	this.pages = [];
	this.tabs = [];
	this.activeTab = 0;
}

Pager.prototype.importPage = function (page, name)
{
	this.pages.push(page);
	this.addTab(name);
}

Pager.prototype.addPage = function (name)
{
	var new_page = document.createElement("ul");
	    new_page.className = "f";
	if (this.pages.length>0) new_page.style.display = "none";
	this.container.appendChild(new_page)
	this.pages.push(new_page);
	this.addTab(name);
	return new_page;
}

Pager.prototype.addTab = function (name)
{
	var pager = this;
	var index = this.tabs.length;
	var tab = hyperlink("javascript:void(0);", name);

	if (GM_getValue("hover", 0)) tab.addEventListener("mouseover", function(){pager.switchTo(index, this);}, false);
	tab.addEventListener("click", function(){pager.switchTo(index, this);}, false);

	if (this.tabs.length>0) this.navigator.appendChild(document.createTextNode(" | "));
	else tab.className += " active";

	this.navigator.appendChild(tab);
	this.tabs.push(tab);
}

Pager.prototype.addElem = function (page, obj)
{
	if (obj==null) return;
	var item = document.createElement("li");
	item.className = "f";
	item.appendChild(obj);
	page.appendChild(item);
}

Pager.prototype.switchTo = function (index, caller)
{
	if (this.activeTab==index) return;
	this.tabs[this.activeTab].className = "a";
	this.pages[this.activeTab].style.display = "none";
	this.activeTab = index;
	this.tabs[this.activeTab].className = "active";
	this.pages[this.activeTab].style.display = "";
	caller.blur();
}




// Now you know how art meets application!

main();