
var globals = {
	config: config,

	scrabble: new ScrabbleRestClient(),       
	viewer: null,
	appdata: {
		uid: null,
		activeMatches: { data: [], length: 0 },
		notifications: [ ]
	}, 
	
	acceptableCountryCodes: ["US","CA","PR"]
	
}

Function.prototype.bind = function(object)
{
	var self = this;
	return function() {
		self.apply(object,arguments);
	}
}

/**
 * The shortcut, not one of those library ;-)
 */
function $(id)
{
	return document.getElementById(id);
}

/**
 * Takes a Date() and return the time from now in english words.
 */
function timeAgoInWords(from)
{
	var now = new Date();
	var mins = Math.round(Math.abs( (from.getTime() - now.getTime()) / 60000));

	if (mins == 0) {
		return 'less than a minute';
	} else if (mins == 1) {
		return '1 minute';
	} else if (mins < 45) {
		return mins + ' minutes';
	} else if (mins < 90) {
		return 'about 1 hour';
	} else if (mins < 1440) {
		return 'about ' + Math.round(mins / 60) + ' hours';
	} else if (mins < 2160) {
		return 'about 1 day';
	} else if (mins < 43200) {
		return Math.round(mins / 1440) + ' days';
	} else if (mins < 86400) {
		return 'about 1 month';
	} else if (mins < 525600) {
		return Math.round(mins / 43200) + ' months';
	} else if (mins < 1051200) {
		return 'about 1 year';
	} else {
		return 'over ' + Math.round(mins / 525600) + ' years';
	}
}

/**
 * Ordinal version of a number
 */
function ordinal(num)
{
	return num + (
		(num % 10 == 1 && num % 100 != 11) ? 'st' :
		(num % 10 == 2 && num % 100 != 12) ? 'nd' :
		(num % 10 == 3 && num % 100 != 13) ? 'rd' : 'th'
	);
}

/**
 * Truncates a string after limit characters,
 * appending ending if truncate occured.
 */
function truncate(str, limit, ending)
{
	ending = ending || '...';
	return (str.length <= limit) ? str : str.substring(0, limit) + ending;
}

/**
 * Content of the last nodeName found under node or empty string by default.
 */
function nodeContent(node, nodeName, defaultValue)
{
	defaultValue = defaultValue || '';
	var nodes = node.getElementsByTagName(nodeName);
	return (nodes.length > 0) ? nodes.item( nodes.length - 1 ).firstChild.nodeValue : defaultValue;
}

/**
 * For an obscure reason the Flash game is encoding title and description sent
 * to the Mayhem.  Note that there is a matching decodeString() function in the
 * ActionScript.
 */
function decodeString(str)
{
	return decodeURIComponent(str).replace(/\+/g, ' ');
}

/**
 * The idea is that an element with id 'n' is considered the button and 'tab_n'
 * is the tab content. A tab can go without a button but must have content.
 * Also, tabs and buttons must be located in the same parent node.
 *
 * Note: We are not using 'gadgets.TabSet' feature because our need are more
 * simple, plus and it's less portable (not available on all containers).
 */
function setSelectedTab(tabId)
{
	var label = $(tabId);
	var tab = $('tab_' + tabId);
	var nodes, i;

	// turn off all tabs before displaying the new one
	nodes = tab.parentNode.childNodes;
	for (i = 0; i < nodes.length; i++) {
		if (nodes[i].nodeType == 1) {
			nodes[i].style.display = 'none';
		}
	}
	tab.style.display = 'block';

	// turn off all buttons before displaying the new one
	if (label) {
		nodes = label.parentNode.childNodes;
		for (i = 0; i < nodes.length; i++) {
			if (nodes[i].nodeType == 1) {
				nodes[i].className = '';
			}
		}
		label.className = 'selected';
	}

}

/**
 * requestNavigatTo shortcut, defaults to canvas view.
 * note: for some reason, myspace use 'CANVAS' and google
 *       'canvas' but surface name is case-sensitive.
 */
function navigateTo(params, view)
{
	return gadgets.views.requestNavigateTo(gadgets.views.getSupportedViews()[ view || 'canvas' ], params);
}

/**
 * getProxyUrl shortcut, sets low refresh interval in debug mode.
 * warning: if opensocial.io.getProxyUrl does not always return URLs 
 * that are on the same domain, Flash wont be able to load users thumbnails.
 * note: refresh interval to 0 is problematic and should not be used.
 */
function proxyUrl(url, params)
{
	params = params || { };
	if (globals.config._debug) {
		params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = 5;
	};
	return gadgets.io.getProxyUrl(url, params);
}

/**
 * Display an error message (or a generic error message if 'force'd or not in
 * debug) and disable the tabbing before throwing an exception.
 */
function die(msg, force)
{
	try {
		//console.warn("DYING: " + msg);
		msg = ( globals.config._debug || force && msg ) ? msg : globals.config._errorMessage;
		$('tab_alert').innerHTML = msg;
		setSelectedTab('alert');
		setSelectedTab = function () { return false; }
	} catch (i) {
		/* gnore */
	}
}

/**
 * Calls 'f'unction by wrapping it's callback in repetitive error checking.
 */
function checked(f)
{
	return function (r) {
		// OpenSocial DataRequest
		var v;
		if (r.get) {
			if (v = r.get('viewer')) {
				if (v.hadError()) {
					if (v.getErrorCode() == 'forbidden' || v.getErrorCode() == '403') {
						//die('This gadget cannot access the information it needs so that you can share or collaborate with friends. Please adjust the gadget\'s settings to enable access.', true);
						//if (/MSIE/.test(navigator.userAgent)) {
							die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_settings.jpg'/></div>", true);
						//} else {
						//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_settings.jpg'/></div>", true);
						//}
						return;
					} else if (v.getErrorCode() == 'unauthorized' || v.getErrorCode() == '401') {
						//die('This gadget cannot access the information it needs so that you can share or collaborate with friends. Please refresh your browser.', true);
						//if (/MSIE/.test(navigator.userAgent)) {
							die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_browser_error.jpg'/></div>", true);
						//} else {
						//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_browser_error.jpg'/></div>", true);
						//}
						return;
					}
				}
				var d = v.getData();
				if (d == null || d.getId() == -1) {
					//die('This gadget cannot access the information it needs so that you can share or collaborate with friends. Please sign in to enable access.', true);
					//if (/MSIE/.test(navigator.userAgent)) {
						die("<div style='{display: block; height: 200px; margin: 0px 0 0 0; padding: 0px 0 0 0; background:#ffffff url(http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_sign_in.jpg) no-repeat top center; text-align:center;}'><a href='https://www.google.com/accounts/ServiceLogin?continue=http://www.google.com/ig&followup=http://www.google.com/ig&service=ig&passive=true&cd=US&hl=en&nui=1&ltmpl=default' target='_parent'><img style='margin: 59px 0 0 -153px; border-style: none; outline: none;' src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_03_btn.jpg'/></a></div>", true);
					//} else {
					//	die("<div style='{display: block; height: 200px; margin: 0px 0 0 0; padding: 0px 0 0 0; background:#ffffff url(http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_sign_in.jpg) no-repeat top center; text-align:center;}'><a href='https://www.google.com/accounts/ServiceLogin?continue=http://www.google.com/ig&followup=http://www.google.com/ig&service=ig&passive=true&cd=US&hl=en&nui=1&ltmpl=default' target='_parent'><img style='margin: 59px 0 0 -153px; border-style: none; outline: none;' src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_03_btn.jpg'/></a></div>", true);
					//}
					return;
				}
			}
		}
		if (r.hadError && r.hadError()) {
			die(r.getErrorMessage());
			return;
		}
		// OpenSocial MakeRequest
		if (r.errors && r.errors.length) {
			// we will assume that 'errors' includes 'oauthError'.
			//die(r.errors.join(', '));
			//die('This gadget cannot access the information it needs so that you can share or collaborate with friends. Please refresh your browser.', true); 
			//if (/MSIE/.test(navigator.userAgent)) {
				die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_browser_error.jpg'/></div>", true);
			//} else {
			//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_browser_error.jpg'/></div>", true);
			//}
			return;
		}
		// actually try to call the function
		try {
			f(r);
		} catch (e) {
			die(e);
		}
	}
}

/**
 * Takes a list of asynchronous style functions (as in last parameter is the
 * callback to call when done) and chain them together.
 * @note: currently commented because not needed, but use as we need decoupling.
function sequencer(calls, args)
{
	if (calls.length == 0) {
		return;
	}
	args = args || [ ];
	function next() {
		sequencer(next.calls, Array.prototype.slice.call(arguments));
	}
	next.calls = calls;
	calls.shift().apply(this, args.concat(next));
}
 */


/**
 * Log where it can (or not).
 * (this function is also called by the Flash)
 */
function dump(msg) {
	try {
		if (!globals.config._debug || !console)
			return;
		if (typeof msg == 'object' && console.dir)
			console.dir(msg);
		if (console.log)
			console.log(msg);
	} catch (i) {
		/* gnore */
	}
}

/**
 * Utility to wipe out AppData, used when developing.
 */
function wipeAppData()
{
	var req = opensocial.newDataRequest();
	req.add(req.newRemovePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, '*'), 'appdata');
	req.send();
}

/**
 * Parse and prepare AppData before using it.
 * (notably by finding notifications and sorting them)
 */
function prepareAppData(appdata)
{
	var i, m, n;
	if (appdata.activeMatches) {
		appdata.activeMatches = gadgets.json.parse(gadgets.util.unescapeString(appdata.activeMatches));
	} else {
		appdata.activeMatches = globals.appdata.activeMatches;
	}
	appdata.notifications = [ ];
	for (i in appdata) {
		if (m = i.match(/^notif-(\d+)/) ) {
			n = gadgets.json.parse(gadgets.util.unescapeString(appdata[i]));
			n.id = i;
			n.timestamp = parseInt(m[1]);
			if (n.isread == 'false') {
				appdata.notifications.push(n);
			}
		}
	}
	appdata.notifications.sort(function (a, b) {
		return a.timestamp > b.timestamp ? -1 : 1;
	});
	return appdata;
}

/**
 * Takes a ScrabbleRestClient.getScrabbleMatchUser response and return
 * an ordered Array of Object with only the needed informations.
 */
function prepareScrabbleMatches(dom)
{
	var matchList = dom.getElementsByTagName('Match');
	var matches = {
		length: matchList.length,
		data: [ ]
	}

	// Extracting needed infos from XML response.
	for (var i = 0; i < matchList.length; i++) {
		var node = matchList.item(i);
		var currentTurnUserId = nodeContent(node, 'currentTurnUserId').replace(/\D+/g, '');
		var matchUserList = node.getElementsByTagName('MatchUser');
		var match = {
			id: nodeContent(node, 'matchURI').replace(/\D/g, ''),
			title: gadgets.util.escapeString( decodeString( nodeContent(node, 'title') ) ),
			yourTurn: currentTurnUserId == globals.appdata.uid,
			lastWord: nodeContent(node, 'word').replace(/[0-9;:,]/g, '') || '-',
			tileBag: nodeContent(node, 'bagsize') || '?',
			lastUpdatedTime: nodeContent(node, 'lastUpdatedTime'),
			isForceForfeitable: false,
			isNudgeable: false
		}

		if (!match.yourTurn) {
			for (var j = 0; j < matchUserList.length; j++) {
				node = matchUserList.item(j);
				if (currentTurnUserId == nodeContent(node, 'userId').replace(/\D+/g, '') && nodeContent(node, 'nudgeAllowed') == 'true') {
					match.isNudgeable = true;
					match.isForceForfeitable = nodeContent(node, 'nudgeCount') > 0;
				}
			}
		}
		matches.data.push(match);
	}

	matches.data.sort(function (a, b) {
		var i, z;
		var attrs = ['yourTurn', 'isNudgeable', 'isForceForfeitable'];
		for (i = 0; i < attrs.length; i++) {
			z = attrs[i];
			if (a[z] && !b[z]) return -1;
			if (b[z] && !a[z]) return 1;
		}
		return (a.lastUpdatedTime > b.lastUpdatedTime) ? -1 : 1;
	});
	matches.data = matches.data.slice(0, 5);

	return matches;
}

/**
 * Basically, just embed the Flash game.
 */
function initCanvas()
{
	if (!gadgets.util.hasFeature('opensocial-0.8')) {
		//die('This gadget requires a feature that is not available in your country at this time. Please check back at a later date.', true);
		//if (/MSIE/.test(navigator.userAgent)) {
			die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_country.jpg'/></div>", true);
		//} else {
		//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_country.jpg'/></div>", true);
		//}
	}
	if (_args()) {
		if (_args().country && _args().lang) {
			// update links in notice to refer to current user locale
			var nodes = $('notice').getElementsByTagName('a');
			for (var i = 0; i < nodes.length; i++) {
				nodes[i].href = nodes[i].href.replace('us/en', _args().country + '/' + _args().lang);
			}
		}
	}
	if (globals.config._messageVisible) {
		$('tab_alert').innerHTML = '<strong>' + globals.config._messageTitle + '</strong><br />' + globals.config._messageContent;
	}
	// The Flash player alt message in here instead of the usual HTML page
	// because we cant embed the SWF when the page is ready but need to wait
	// for OpenSocial to give us the viewer.getId()
	if (!swfobject.hasFlashPlayerVersion(globals.config._playerTarget)) {
		$('scrabbleFlashContent').innerHTML = '<strong>Flash Player upgrade required</strong><p>You must download and install the latest version of the Adobe Flash Player to view this content <a href="http://get.adobe.com/flashplayer" target="_blank">Download Flash</a>.</p>';
		return;
	}

	initViewer(function () {
		globals.scrabble.verifyIP(function(result) {
		
			var country = "";
			var acceptableCountry = false;
			
			//grab the country code
			if(result.data.documentElement) {
				if(result.data.documentElement.childNodes) {
					var nodeArr = result.data.documentElement.childNodes;
					for(var k = 0; k<nodeArr.length; k++) {
						targetNode = nodeArr[k];
						if(targetNode.nodeName == "country") {
							country = targetNode.firstChild.nodeValue;
						}
					}
				}
			} 

			if(country == "") {
				//there was an error, so die
				die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://devmayhem.eamobile.com:8443/ijames_scrabble_v2_dev/scrabble_igoogle_06_browser_error.jpg'/></div>", true, "embedding game");
			}
	
			for(var i = 0; i<globals.acceptableCountryCodes.length; i++) {
				if(globals.acceptableCountryCodes[i] == country) {
					acceptableCountry = true;
				}
			}
			
			//console.warn("Country: " + country);
			//console.warn("Country acceptable: " + acceptableCountry );
	
			if(acceptableCountry) {
	
				var flashvars, params;
				var query = '';
				var queries = gadgets.views.getParams();
		
				for (var i in queries) {
					query += i + '=' + queries[i] + '&';
				}
				
				globals.config._gameSwfUrl = proxyUrl(globals.config._gameSwfUrl);
				//globals.config._gameSwfUrl = proxyUrl(globals.config._gameSwfUrl + "?t=" + (new Date()).getTime());
		
				flashvars = {
					scrabbleDiv: 'scrabbleFlashContent',
					config: escape( gadgets.json.stringify(globals.config) ),
					userId: globals.viewer.getId(),
					socialPlatformName: globals.config._socialPlatformName,
					divId: 'scrabbleFlashContent',
					query: query
				}
				params = {
					align: 'middle', 
					quality: 'high', 
					bgcolor: '#ffffff', 
					allowfullscreen: 'false', 
					height: '615', 
					width: '593', 
					wmode: 'opaque', 
					allowScriptAccess: 'always', 
					debug: globals.config._debug
				}
				swfobject.embedSWF(
					globals.config._gameSwfUrl,
					flashvars.divId,
					params.width, params.height, globals.config._playerTarget,
					null,
					flashvars,
					params,
					{ id: flashvars.divId, name: flashvars.divId }
				);
				$(flashvars.divId).style.outline = 'none'; // prevent the border in ff 3
			
			} else {
				//die with an invalid country message
				die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_country.jpg'/></div>", true);
			}
			
		});
	});
}

/**
 * Homepage entry point.
 */
function initHome()
{
	if (!gadgets.util.hasFeature('opensocial-0.8')) {
		//die('This gadget requires a feature that is not available in your country at this time. Please check back at a later date.', true);
		//if (/MSIE/.test(navigator.userAgent)) {
			die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_country.jpg'/></div>", true);
		//} else {
		//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_country.jpg'/></div>", true);
		//}
	}
	if (navigator.userAgent.match(/^Mozilla\/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1; SV1;/)) {
		//die('The version of the browser you are using is not compatible with the current version of Scrabble, please update your browser to a newer version.', true);
		//if (/MSIE/.test(navigator.userAgent)) {
			die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_compatible.jpg'/></div>", true);
		//} else {
		//	die("<div style='{margin: 0px 0 0 0; padding: 0; background:#ffffff; height: 100%; text-align: center;}'><img src='http://webtestsngames.eamobile.com/mayhem/scrabble/igoogle/scrabble_igoogle_06_compatible.jpg'/></div>", true);
		//}
	}
	initHomeBehavior();
	setInterval(initHomeBehavior, 900000);
}

/**
 * Bind functions to some events of the elements on page.
 */
function initHomeBehavior()
{
	var nodes = $('tabset').childNodes;
	for (var i = 0; i < nodes.length; i++) {
		if (nodes[i].nodeType != 1) {
			continue;
		}
		nodes[i].onclick = function () {
			setSelectedTab(this.id);
			initScroller();
			return false;
		}
	}
	$('view_all_games').onclick = function () {
		navigateTo();
		return false;
	}
	$('create_games').onclick = $('new_game').onclick = function () {
		navigateTo({ playwithid: '-1' });
		return false;
	}
	$('share_gadget').onclick = function () {
		opensocial.requestShareApp(opensocial.IdSpec.PersonId.VIEWER_FRIENDS, opensocial.newMessage(this.innerHTML));
		return false;
	}
	initViewer(initScrabbleId);
}

/**
 * Ajust the scroller and attach events.
 */
function initScroller()
{
	var content = $('tabs');
	var scrollarea = $('tab_notifications_content');

	scrollarea.style.height = 'auto';

	var currentTop = parseInt(scrollarea.style.top) || 0;
	var maxBottom = - (scrollarea.offsetHeight - content.offsetHeight);

	if (scrollarea.offsetHeight <= content.offsetHeight) {
		scrollarea.style.top = '0px';
		// $('scrollUp').style.visibility = 'hidden';
		// $('scrollDown').style.visibility = 'hidden';
		return;
	} else if (currentTop < maxBottom) {
		scrollarea.style.top = maxBottom + 'px';

	}

	var myScroller = new Scroller(content, scrollarea);
	var btn = $('scrollUp');
	btn.style.visibility = 'visible';

	btn.onmouseover = function() {
		myScroller.stepPixels = 2;
		myScroller.startUp();
	}
	btn.onmousedown = function() {
		myScroller.stepPixels = 6;
	}
	btn.onmouseup = function() {
		myScroller.stepPixels = 2;
	}
	btn.onmouseout = function() {
		myScroller.stop();
	}

	var btn = $('scrollDown');
	btn.style.visibility = 'visible';

	btn.onmouseover = function() {
		myScroller.stepPixels = 2;
		myScroller.startDown();
	}
	btn.onmousedown = function() {
		myScroller.stepPixels = 6;
	}
	btn.onmouseup = function() {
		myScroller.stepPixels = 2;
	}
	btn.onmouseout = function() {
		myScroller.stop();
	}
}

/**
 * Retrieve viewer and its AppData into the globals.
 */
function initViewer(cb)
{
	var req = opensocial.newDataRequest();
	req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
	req.add(req.newFetchPersonAppDataRequest(opensocial.newIdSpec({ 'userId' : 'VIEWER' }), '*'), 'appdata');
	req.send(checked(function (r) {
		globals.viewer = r.get('viewer').getData();
		globals.appdata = prepareAppData( r.get('appdata').getData()[ globals.viewer.getId() ] );
		cb(opensocial.newDataRequest());
	}));
}

/**
 * Try to put userId is in the AppData.
 */
function initScrabbleId(req)
{
	if (!globals.config._debug && globals.appdata.uid) {
		initActiveMatches(req);
	 	return;
	}
	globals.scrabble.getUser(function (r) {
		if ( !(r.data.firstChild.nodeName.toLowerCase() == 'error' && r.data.firstChild.getAttribute('code') == '404') ) {
			globals.appdata.uid = nodeContent(r.data, 'URI').replace(/\D/g, '');
			req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'uid', globals.appdata.uid), 'set_uid');
		}
		initActiveMatches(req);
	});
}

/**
 * Make sure that active matches are uptodate.
 */
function initActiveMatches(req)
{
	if ( // could not load anyway
	     (!globals.appdata.uid) ||
	     // user have not played any turn in the canvas
	     (globals.appdata.matchesUpdated && globals.appdata.matchesUpdated == 'false' &&
	     (globals.appdata.notifications.length == 0 || globals.appdata.lastActiveMatchesUpdate == globals.appdata.notifications[0].timestamp)) ) {
		initDisplay(req);
		return;
	}
	
	//console.info('retrieving token with uid: ' + globals.appdata.uid);

	globals.scrabble.getToken('/users/' + globals.appdata.uid, function (r) {
	
		//console.info('token response: ' + (new XMLSerializer()).serializeToString(r.data));
	
		if (r.data.firstChild.nodeName == 'waitInLine') {
			var pos = nodeContent(r.data, 'myPlaceInLine');
			pos = (pos == 1) ? 'next' : ordinal(pos);
			$('tab_alert').innerHTML = '<strong>Scrabble is Full...</strong><br />There is no spots left to play Scrabble at this moment. you are currently ' + pos + ' in line to launch Scrabble.';
			setSelectedTab('alert');
			return;
		}
		
		//console.info('retrieving matches');
		
		globals.scrabble.getScrabbleMatchUser(nodeContent(r.data, 'sessionKey'), function (r) {
		
			//console.info('matches response: ' + (new XMLSerializer()).serializeToString(r.data));
		
			globals.appdata.activeMatches = prepareScrabbleMatches(r.data);
			req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'activeMatches', gadgets.json.stringify(globals.appdata.activeMatches), 'set_active_matches'));
			var amu = (globals.appdata.notifications.length > 0) ? globals.appdata.notifications[0].timestamp : 0;
			req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'lastActiveMatchesUpdate', amu, 'set_last_active_matches_update'));
			req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'matchesUpdated', 'false'), 'set_matches_updated');
			initDisplay(req);
		});
	});
}

/**
 * Initialized the display based on the data that has been fetched.
 */
function initDisplay(req)
{
	req.send(); // we ignore if updates have succeed, on error we will just refetch

	/* display logo depending on window size
	 */
	var bgOrig = $('tabset').style.backgroundImage;
	window.onresize = function () {
		$('tabset').style.backgroundImage = $('gadget').offsetWidth < 330 ? 'none' : bgOrig;
	}
	window.onresize();

	/* update activeMatches
	 */
	var matches = globals.appdata.activeMatches;
	var matchArray = matches.data;
	var tr, td;
	var columns = [ 'yourTurn', 'title', 'lastWord', 'tileBag' ];
	var body = $('tab_games_body');
	$('games_count').innerHTML = matches.length;
	$('games_count2').innerHTML = matches.length;

	while (body.firstChild) {
		body.removeChild(body.firstChild);
	}

	for (var i = 0; i < matchArray.length; i++) {
		tr = document.createElement('tr');
		tr._matchId = matchArray[i].id;
		tr.onclick = function () {
			navigateTo({ matchId: this._matchId });
		}
		for (var j = 0; j < columns.length; j++) {
			td = document.createElement('td');
			td.className = columns[j];
			var val = matchArray[i][ columns[j] ];
			if (columns[j] == 'title') {
				val = truncate(val, 12);
			} else if (columns[j] == 'lastWord') {
				val = truncate(val, 8);
			}
			td.innerHTML = gadgets.util.escapeString(val);
			tr.appendChild(td);
		}

		// slightly modify yourTurn class to CSS it up.
		if (matchArray[i].isForceForfeitable) {
			tr.childNodes[0].className += ' forfeit';
		} else if (matchArray[i].isNudgeable) {
			tr.childNodes[0].className += ' nudgable';
		} else if (matchArray[i].yourTurn) {
			tr.childNodes[0].className += ' playTurn';
		}

		body.appendChild(tr);
	}
	if (matchArray.length == 0) {
		$('tab_games_empty').style.display = 'block';
		body.parentNode.style.display = 'none';
	} else {
		$('tab_games_empty').style.display = 'none';
		$('create_games').style.display = 'inline';
	}
	if ($('tab_alert').offsetHeight > 0) { // initial call
		setSelectedTab('games');
	}

	/* update notifications
	 */
	$('tab_notifications_content').innerHTML = '';
	var html = [ ];
	for (var i = 0; i < globals.appdata.notifications.length; i++) {
		var n = globals.appdata.notifications[i];
		var p = document.createElement('p');
		p._id = n.id;
		p.className = i % 2 ? 'odd' : 'even';
		p.innerHTML = n.text;
		p.innerHTML += ' <a href="javascript:void(0);" class="del" title="Delete">Delete</a>';
		p.innerHTML += ' <span class="timeago">' + timeAgoInWords(new Date(n.timestamp)) + '</span>';
		var anchors = p.getElementsByTagName('a');
		for (var j = 0; j < anchors.length; j++) {
			anchors[j].onclick = function () {
				this.onclick = null; // make sure it is not clickable twice
				var self = this;
				var notif = gadgets.json.parse(gadgets.util.unescapeString(globals.appdata[this.parentNode._id]));
				notif.isread = true;
				notif = gadgets.json.stringify(notif);
				var req = opensocial.newDataRequest();
				req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, this.parentNode._id, notif), 'set_notifications');
				req.send(function () {
					if (self.className == 'del') {
						self.parentNode.style.display = 'none';
						$('notifications_count').innerHTML = parseInt($('notifications_count').innerHTML) - 1;
						initScroller();
					}
				});
				return true;
			}
		}
		$('tab_notifications_content').appendChild(p);
	}
	$('notifications_count').innerHTML = globals.appdata.notifications.length;
	$('tab_notifications_empty').style.display = globals.appdata.notifications.length ? 'none' : 'block';
	initScroller();
}


/**
 * Based on some other code (I think the PHP originaly found)
 * The methods here are only for the gadget here,
 * a more generic version would imply rewrite.
 */
function ScrabbleRestClient()
{
	var sessionKey;
    
	function mkr(met, uri, cb, params) {
		met = met.toUpperCase();
		uri = globals.config._mayhemUrl + uri;
		params = params || { };
		params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
		params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;
		params[gadgets.io.RequestParameters.HEADERS] = {
			mh_auth_method: globals.config._mayhemAuthMethod,
			mh_auth_params: globals.config._mayhemAuthParams,
			mh_method_override: met,
			mh_client_version: globals.config._socialPlatformName + '-' + globals.config._version
		};
		if (globals.appdata.uid)
			params[gadgets.io.RequestParameters.HEADERS]['mh_uid'] = globals.appdata.uid;
		if (sessionKey)
			params[gadgets.io.RequestParameters.HEADERS]['mh_session_key'] = sessionKey;
		if (met == 'POST')
			params[gadgets.io.RequestParameters.POST_DATA]= gadgets.io.encodeValues(params);
		gadgets.io.makeRequest(uri, checked(cb), params);
	}

	this.getToken = function(uri, cb) {
		var params = { };
		params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
		mkr('get', uri + '/token', cb, params);
	}

	this.getUser = function (cb) {
		mkr('get', '/users?application=' + globals.config._socialPlatformName + '&applicationUserId=' + globals.viewer.getId(), cb);
	}

	this.postUser = function (cb) {
		var params = { };
		params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
		params['application'] = globals.config._socialPlatformName;
		params['applicationUserId'] = globals.viewer.getId();
		mkr('post', '/users', cb, params);
	}

	this.getScrabbleMatchUser = function (session_key, cb) {
		sessionKey = session_key;
		var now = (new Date()).getTime(); // google cache the queries, force him not!
		mkr('get', '/games/scrabble/users/' + globals.appdata.uid + '/scrabblematches?clientFilter=FILTER_RECENT&t=' + now, cb);
	}
	
	this.verifyIP = function (cb) {
		var now = (new Date()).getTime();
		mkr('get', '/iplocation?t=' + now, cb);
	}
	
}


function Scroller(container, scrollarea)
{
	var MOVE_UP = 1;
	var MOVE_DOWN = -1;
	var direction = MOVE_UP;
	var timer = null;
	var MAX_UP = 0;
	var MAX_DOWN = container.offsetHeight - scrollarea.offsetHeight;
	this.stepMilliseconds = 20;
	this.stepPixels = 2;
	scrollarea.style.top = scrollarea.offsetTop + 'px';

	this.up = function() { direction = MOVE_UP; }
	this.down = function() { direction = MOVE_DOWN; }
	this.startUp = function() { this.up(); this.start();}
	this.startDown = function() { this.down(); this.start();}
	this.start = function() {
		this.stop();
		window.__scroller = this.step.bind(this);
		timer = setInterval('__scroller()', this.stepMilliseconds);
	}
	this.stop = function(){ clearInterval(timer); }
	this.step = function() {
		var mouvement = parseInt(scrollarea.style.top) + (direction * this.stepPixels);
		if (mouvement <= MAX_DOWN) {
			mouvement = MAX_DOWN;
			this.stop();
		} else if (mouvement >= MAX_UP) {
			mouvement = MAX_UP;
			this.stop();
		}
		scrollarea.style.top = mouvement + 'px';
	}
}



