window.AutoGraph = (function () {
	var head = document.getElementsByTagName('head')[0],
		done = false,
		userdata = {
			name: '',
			avatar: '',
			location: '',
			username: '',
			services: {},
			contacts: {}
		},
		known_services = {
			'twitter.com': {name: 'Twitter', type: ['microblog']},
			'flickr.com': {name: 'Flickr',  type: ['photo']},
			'facebook.com': {name: 'Facebook', type: ['microblog', 'photo', 'video', 'bookmarks', 'sn']},
			'huffduffer.com': {name: 'Huffduffer', type: ['music']},
			'dopplr.com': {name: 'dopplr', type: ['travel']},
			'friendfeed.com': {name: 'Friendfeed', type: ['microblog', 'aggregator']},
			'del.icio.us':  {name: 'del.icio.us', type: ['links']},
			'ma.gnolia.com':  {name: 'ma.gnolia', type: ['links']},
			'myspace.com':  {name: 'MySpace', type: ['photo', 'video', 'sn']},
			'pownce.com':  {name: 'Pownce', type: ['photo', 'video', 'music', 'links', 'microblog']},
			'upcoming.yahoo.com': {name: 'Upcoming', type: ['events']},
			'last.fm': {name: 'Last.fm', type: ['music']},
			'linkedin.com': {name: 'LinkedIn', type: ['sn']},
			'myopenid.com': {name: 'myOpenID', type: ['openid']},
			'claimid.com': {name: 'ClaimID', type: ['openid']},
			'jaiku.com': {name: 'Jaiku', type: ['microblog']},
			'youtube.com': {name: 'YouTube', type: ['video']},
			'marela.si': {name: 'Marela', type: ['photo']},
			'koornk.com': {name: 'Koornk', type: ['microblog']}
		},
		mJSON = function () {
			function f(n) {
				return n >= 10 ? n : '0' + n;
			}
			var defaults = {
					'date':function (t) {
						return t.getUTCFullYear() + '-' +
						f(t.getUTCMonth() + 1)    + '-' +
						f(t.getUTCDate())         + 'T' +
						f(t.getUTCHours())        + ':' +
						f(t.getUTCMinutes())      + ':' +
						f(t.getUTCSeconds())      + 'Z';
					},
					'other':function (t) {return t.valueOf();}
				},
				escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
					gap,
					meta = {
						'\b': '\\b',
						'\t': '\\t',
						'\n': '\\n',
						'\f': '\\f',
						'\r': '\\r',
						'"' : '\\"',
						'\\': '\\\\'
					};
			function quote(string) {
				escapeable.lastIndex = 0;
				return escapeable.test(string) ?
					'"' + string.replace(escapeable, function (a) {
						var c = meta[a];
						if (typeof c === 'string') {
							return c;
						}
					return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
					}) + '"' :
					'"' + string + '"';
			}
			function str(key, holder) {
				var i,k,v,length,mind=gap,partial,value=holder[key];
				if (value && typeof value === 'object') {
					if (typeof value.toJSON === 'function' && typeof window.Prototype === 'undefined') { // when prototype present we ignore its broken toJSON methods
						value = value.toJSON(key);
					} else if (value.constructor === Date) {
						value = defaults.date(value,key);
					} else if (value.constructor === Number || value.constructor === String || value.constructor === Boolean) {
						value = defaults.other(value,key);
					}
				}
				switch (typeof value) {
					case 'string':
						return quote(value);
					case 'number':
						return isFinite(value) ? String(value) : 'null';
					case 'boolean':
					case 'null':
						return String(value);
					case 'object':
						if (!value) {
							return 'null';
						}
						partial = [];
						if (typeof value.length === 'number' && !value.propertyIsEnumerable('length')) {
							length = value.length;
							for (i = 0; i < length; i += 1) {
								partial[i] = str(i, value) || 'null';
							}
							v = partial.length === 0 ? '[]' :
								gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
							gap = mind;
							return v;
						}
						for (k in value) {
							if (Object.hasOwnProperty.call(value, k)) {
								v = str(k, value);
								if (v) {
									partial.push(quote(k) + (gap ? ': ' : ':') + v);
								}
							}
						}
						v = partial.length === 0 ? '{}' :
							gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
						gap = mind;
						return v;
				}
			}
			return {
				stringify: function (value) {
					return str('', {'': value});
				},
				parse: function (str) {
					try {
						return eval('(' + str + ')');
					} catch (er) {
						throw new Error('Parsing JSON failed.');
					}
				}
			};
		}();

	if (!Array.prototype.indexOf) {
	  Array.prototype.indexOf = function(elt, from) {
	    var len = this.length;
	    from = Number(from) || 0;
	    from = from < 0? Math.ceil(from) : Math.floor(from);
	    if (from < 0) {
	    	from += len;
	    }
	    for (; from < len; from += 1) {
	      if (from in this && this[from] === elt) {
	        return from;
	      }
	    }
	    return -1;
	  };
	}
	
	function loadScript(src) {
		var scr = head.appendChild(document.createElement('script'));
		scr.type = 'text/javascript';
		scr.src = src;
	}
	
	function add(type, key, value) {
		if (value.constructor === Object) {
			value = mJSON.stringify(value);
		}
		if (userdata[type][key]) {
			if (userdata[type][key].constructor === Array && userdata[type][key].indexOf(value) < 0 || userdata[type][key].constructor !== Array && userdata[type][key] !== value) {
				userdata[type][key] = [].concat(userdata[type][key]).concat(value);				
			}
		} else {
			userdata[type][key] = value;
		}
	}
	
	function extractHostname(url) {
		var e, m, hostname;
		e = arguments.callee.e = arguments.callee.e || /\/\/((?:www)\d*\.){0,1}(.*?)\//;
		m = url.match(e);
		if (m) {
			return m[2].indexOf('.')>=0? m[2] : m[1]+m[2];
		} else {
			return '';
		}
	}
	
	function getTwitter(u, cb) {
		window.getTwitterData = function (data) {
			if (!data.error) {
				userdata.name = data[0].user.name;
				userdata.avatar = data[0].user.profile_image_url;
				userdata.location = data[0].user.location;
				userdata.username = data[0].user.screen_name;
				add('services', 'twitter.com', 'http://twitter.com/' + data[0].user.screen_name);
			}
			if (typeof cb === 'function') {
				cb(data);
			}
		};
		loadScript((u? 'http://twitter.com/statuses/user_timeline/' + u + '.json' : 'http://twitter.com/statuses/user_timeline.json') + '?count=1&callback=getTwitterData&suppress_response_codes');
	}
	
	function getGraph(url, cb) {
		window.getGraphData = function (data) {
			var nodeName = '', 
				claimedNodes = [], nodesReferenced = [],
				i = 0, j = 0,
				hostname = '';
			for (nodeName in data.nodes) {
				// services
				claimedNodes = data.nodes[nodeName].claimed_nodes;
				for (i = 0, j = claimedNodes.length; i < j; i += 1) {
					hostname = extractHostname(claimedNodes[i]);
					if (known_services[hostname]) {
						add('services', hostname, claimedNodes[i]);
					} else if (hostname.split('.').length > 2 && known_services[hostname.substr(hostname.indexOf('.') + 1)]) {
						add('services', hostname.substr(hostname.indexOf('.') + 1), claimedNodes[i]);
					}
				}
				// contacts
				nodesReferenced = data.nodes[nodeName].nodes_referenced;
				for (hostname in nodesReferenced) {
					if (nodesReferenced[hostname].types.indexOf('me') < 0) {
						add('contacts', hostname, nodesReferenced[hostname]);
					}
				}
			}
			if (typeof cb === 'function') {
				cb(data);
			}
		};
		loadScript('http://socialgraph.apis.google.com/lookup?q=' + url + '&edo=1&fme=1&callback=getGraphData');
	}
	
	function getData(u, cb) {
		if (done) {
			cb(userdata, 'success');
		} else {
			getTwitter(u, function (data) {
				if (data.error) {
					cb(data, 'error');
				} else {
					getGraph('http://twitter.com/' + data[0].user.screen_name, function () {
						done = true;
						cb(userdata, 'success');
					});
				}
			});
		}
	}
	
	return {
		extractHostname: extractHostname,
		services: known_services,
		getData: getData
	};
})();
