Helgon.net avatars in user listings

By Henrik N Last update Aug 23, 2005 — Installed 1,627 times.
// ==UserScript==
// @name			Helgon.net avatars in user listings
// @namespace		http://henrik.nyh.se
// @description		Show avatars next to user names in Helgon.net user listings.
// @include			http://*helgon.net/*Visitors.asp*
// @include			http://*helgon.net/*Views.asp*?*
// @include			http://*helgon.net/search/Search.asp*?*
// @include			http://*helgon.net/Start/online.asp*
// @include			http://*helgon.net/Start/BirthDay.asp*
// @include			http://*helgon.net/Start/lastreg.asp*
// @include			http://*helgon.net/UserInfo/Userinfo_Lastvisits.asp*
// @include			http://*helgon.net/*whomonitors.asp*
// @include			http://*helgon.net/Diary/Diary_readOK.asp*
// @include			http://*helgon.net/Friends/Friends.asp*
// ==/UserScript==

// TODO: Instead of a million @includes, JS it (would handle case variations).
// TODO: Remove theoretical risk of writing timestamp to cache for users with proper avatars.
// TODO: Don't read friend page #1 twice. Low priority as the avatar is cached thenceafter.

// Avatars are retrieved in a roundabout way to avoid leaving a visitor log trail.
// If this is not a concern, the code could be much simplified (by retrieving from profiles only).

var avatar_rexp = new RegExp("http://(.+\.)?helgon\.net/u/\.*?\.jpg");
var profile_links_xp = "//A[@target='helgonmain'][starts-with(@href, '/userinfo/userinfo.aspx?ID=')]";
var expire_cache_in_millisecs = 1000*60*60*6  // 6 hour timeout until re-checking avatar-less users for avatars

// Helgon is subdomain sensitive; get it right
var base_url = 'http://' + location.host;

// Initiate cache from GM_value
var defer_write_cache, cache = eval(GM_getValue("cache")) || {};

with_each(profile_links_xp, function(link) {
	link.id = link.href.match(/\?ID=(\d+)/i)[1];  // Set id of link to id of user
	get_avatar_for(link.id);
});


/* Specific functions */

function cache_later(id, avatar) {
	cache[id] = avatar;
	
	// Write cache to GM_value, but not needlessly often (thanks, Johan Sundström)
	if (defer_write_cache) clearTimeout(defer_write_cache);
	defer_write_cache = setTimeout(write_cache, 2e3);  // Write in two seconds
}

function write_cache() {
	GM_setValue("cache", cache.toSource());
}

function extract_avatar_url(contents) {
	var match = contents.match(avatar_rexp);
	if (match) return match[0];
}

function get_avatar_for(id) {
	var cached_data = get_cached_avatar_for(id);
	if (typeof(cached_data) == "string") {
		display_avatar_for(cached_data, id);
	} else {
		if (typeof(cached_data) == "number" && (cached_data + expire_cache_in_millisecs) > (new Date).getTime())
			return;  // If a stored timestamp (for avatar-less users), bail if we recently looked for an avatar
		get_new_avatar_for(id);
	}
}

function get_cached_avatar_for(id) {
	return cache[id];
}

function get_new_avatar_for(id) {
	(function(id) {  // Bind id
		cache_later(id, (new Date).getTime());  // Set current timestamp as default, to avoid re-retrieving for avatar-less users
		get_new_avatar_from_makefriends(id);  // Try this first; that function triggers others if necessary	
	})(id);
}

function get_new_avatar_from_makefriends(id) {
	var url = base_url + "/friends/friends_reg.asp?ID=0&UserID=" + id + "&Action=new";
	get(url, function(contents) {
		var avatar = extract_avatar_url(contents); 
		if (avatar) {
			set_new_avatar_for(avatar, id);
		} else {
			if (contents.match(/\bredan polare\b/))
				get_new_avatar_from_friends(id);
			else if (contents.match(/\binte bli polare med dig\b/))
				get_new_avatar_from_self(id);
		}
	});
}

function get_new_avatar_from_friends(id) {
	var url = base_url + "/Friends/Friends.asp";
	
	get(url, function(contents) {
		var m = contents.match(/Visar sida:.*&Page=(\d+)/i);  // Multiple pages?
		var max_page = m ? m[1] : 1;
		for (var i = 1; i <= max_page; i++) {
			url = url + "?Page=" + (i==1 ? '' : i);  // ?Page=1 times out, so do just ?Page=
			get(url, function(contents) {
				m = contents.match(new RegExp('friends_info\\.asp\\?ID=(\\d+)&UserID=(\\d+)&UserID2=('+id+')\\b', 'i'));
				if (m) {
					url = base_url + "/Friends/friends_info.asp?ID="+m[1]+"&UserID="+m[2]+"&UserID2="+m[3];
					get(url, function(contents) {
						var line = contents.match(new RegExp('.*\\?ID='+id+'.*<img.*', 'i'))[0];
						var avatar = extract_avatar_url(line);
						set_new_avatar_for(avatar, id);
					});
				}
			});
		}

	});
}

function get_new_avatar_from_self(id) {
	var url = base_url + "/UserInfo/UserInfo.asp";
	get(url, function(contents) {
		var avatar = extract_avatar_url(contents); 
		if (avatar) {
			set_new_avatar_for(avatar, id);
		}
	});	
}

function set_new_avatar_for(avatar, id) {
	if (avatar.indexOf('000/{00000000-0000-0000-0000-0000000000') != -1) return;  // We only want non-default pictures
	cache_later(id, avatar);
	display_avatar_for(avatar, id);
}

function recache_avatar() {
	var id = this.id.replace('avatar-', '');
	delete cache[id];
	this.parentNode.removeChild(this);
	get_new_avatar_for(id);
}

function display_avatar_for(avatar, id) {
	// You set us up the avatar!
	var img = document.createElement('img');
	img.id = "avatar-" + id;
	var url = "/userinfo/userinfo.asp?ID=" + id;
	with (img) {
		src = avatar;
		className = 'largeimageborder';
		style.width = '40px';
		style.height = '56px';
		align = 'middle';
		addEventListener('error', recache_avatar, true);
	}

	// You set us up the spacing! The text node!!
	var space = document.createTextNode(' ');
	
	// You set us up the link!
	var link = document.createElement('a');
	with (link) {
		href = url;
		appendChild(img);
	}				
	
	if (avatar.indexOf('000/{00000000-0000-0000-0000-0000000000') == -1) { // Only for non-default pictures
		$(id).parentNode.insertBefore(space, $(id));  // Insert spacing
		$(id).parentNode.insertBefore(link, space);  // Insert linked image
	}
}


/* General functions */

function $(id) { return document.getElementById(id); }
function xp(query, root) { return document.evaluate(query, root || document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); }
function with_each(query, cb, root) {
	var results = xp(query, root);
	for (var i = 0, j = results.snapshotLength; i < j; i++)
		cb(results.snapshotItem(i));
}

function get(url, cb) {
  GM_xmlhttpRequest({
	method: "GET",
	url:url,
    onload:function(xhr){ cb(xhr.responseText ); }
  });
}