/**
* Find an arbitrary number of elements by their id, and return an array of
* the ones that exist.
*/
function $all(){
	return $A($.apply(null, $A(arguments))).compact();
}

/**
* Get the current time in UTC format.
*/
Date.getUTCTime = function(options){
	options = options || {};
	var date = new Date();
	if (options.now) {
		date.setTime(options.now);
	}
	var utc = {
		yyyy: date.getUTCFullYear(),
		mm: date.getUTCMonth() + 1,
		dd: date.getUTCDate(),
		hh: date.getUTCHours(),
		mn: date.getUTCMinutes(),
		ss: date.getUTCSeconds()
	};
	for (var key in utc) {
		utc[key] = String(utc[key]);
		if (utc[key].length < 2) {
			utc[key] = '0' + utc[key];
		}
	}
	return utc.yyyy + '-' + utc.mm + '-' + utc.dd + 'T' + utc.hh + ':' + utc.mn + ':' + utc.ss + 'Z';
};

/**
* Get the current timestamp.
*/
Date.now = function(){
	return new Date().getTime();
};

/**
* Convert milliseconds to words.
* @return {String} Description of the duration:
*	if <= 1000ms, 'a second'
*	if <= 60s, 'a minute'
*	if < 60m, 'x minutes'
*	if < 24h, 'x hours'
*	else 'x days'
*/
Number.prototype.durationToWords = function(){
	var i = Number(this);
	if (i <= 1000)
		return 'a second';
	if ((i = Math.floor(i / 1000)) <= 60)
		return 'a minute';
	if ((i = Math.floor(i / 60)) < 60)
		return i + ' minutes';
	if ((i = Math.floor(i / 60)) < 24)
		return i + ' hour' + (i > 1 ? 's' : '');
	i = Math.floor(i / 24);
	return i + ' day' + (i > 1 ? 's' : '');
};

/**
* Convert milliseconds to days.
* @return {String} Description of the duration:
*	'x days'
*/
Number.prototype.durationToDays = function(){
	var i = Number(this);
	//I used Math.floor() to mimic PHP's round()
	i = Math.floor(Math.round(i / 1000 / 60 / 60 / 24));
	return i + ' day' + (i > 1 ? 's' : '');
};

/**
* Performs a case insensitive comparison of two strings. If the parameter
* is a boolean or number, it will be cast to a string before comparison.
* All other non-string types will always return false.
*/
String.prototype.equals = function(s){
	var type = typeof s;
	if (type != 'string') {
		if (type == 'boolean' || type == 'number')
			s = String(s);
		else
			return false;
	}
	return this.toLowerCase() == s.toLowerCase();
};

/**
* Append the version query string to the end of a url.
*/
String.prototype.withRandomQueryString = function(){
	var params = this.split('?');
	var base = params.shift();
	params = params.reduce() || [];
	if (typeof params == 'string')
		params = params.split('&');
	return base + '?' + params.concat(GG.config.debug.active ? Date.now() : GG.config.version).join('&');
};

/**
* Base64 decoding and encoding.
*/
Object.extend(String.prototype, function(){
	var key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	
	return {
		decode64: function(){
			var input = String(this);
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
			do {
				enc1 = key.indexOf(input.charAt(i++));
				enc2 = key.indexOf(input.charAt(i++));
				enc3 = key.indexOf(input.charAt(i++));
				enc4 = key.indexOf(input.charAt(i++));
				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;
				output = output + String.fromCharCode(chr1);
				if (enc3 != 64)
					output = output + String.fromCharCode(chr2);
				if (enc4 != 64)
					output = output + String.fromCharCode(chr3);
			} while (i < input.length);
			return output;
		},
		
		encode64: function(){
			var input = String(this);
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;
			do {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);
				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;
				if (isNaN(chr2))
					enc3 = enc4 = 64;
				else if (isNaN(chr3))
					enc4 = 64;
				output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
			} while (i < input.length);
			return output;
		}
	};
}());

/**
* Rot13 encoding.
*/
String.prototype.rot13 = function(){
	var s = String(this);
	var rot = function(t, u, v){ return String.fromCharCode(((t - u + v) % (v * 2)) + u); };
	var a = 'a'.charCodeAt(), z = a + 26, A = 'A'.charCodeAt(), Z = A + 26;
	var b = [];
	for (var i = s.length - 1; i >= 0; i--) {
		var c = s.charCodeAt(i);
		if (c >= a && c < z)
			b[i] = rot(c, a, 13);
		else if (c >= A && c < Z)
			b[i] = rot(c, A, 13);
		else
			b[i] = s.charAt(i);
	}
	return b.join('');
};

/**
* Like Rot13, but accepts an arbitrary rotation and preserves capitalizations, vowels, and consonants.
*/
String.prototype.rotate = function(offset) {
	var s = String(this);
	if (typeof offset == 'undefined')
		offset = 16;
	var sets = ['aeiou'.split(''), 'AEIOU'.split(''), 'bcdfghjklmnpqrstvwxyz'.split(''), 'BCDFGHJKLMNPQRSTVWXYZ'.split('')];
	var encoded = [];
	for (var i = 0; i < s.length; i++) {
		var c = s.charAt(i);
		for (var j = 0; j < sets.length; j++) {
			var k = sets[j].indexOf(c);
			if (k != -1) {
				encoded.push(sets[j][(k + offset) % sets[j].length]);
				break;
			}
		}
		if (j == sets.length)
			encoded.push(c);
	}
	return encoded.join('');
};




// script.aculo.us Effect.Modal.js
// Copyright(c) 2007 - InstantAction.com
//
// Effect.Modal is cool
/* Helper Effect for makeing free-floating windows
 */
var Popup = Class.create();
Object.extend(Popup.prototype,
{
	initialize: function(element)
	{
		this.wrapper = $(document.createElement('div'));
		document.body.appendChild(this.wrapper);
		this.content = $(element);
		if(!this.content) {
			alert('You did not specify any content for the window!');
		}
		
		this.options = Object.extend({ centered: true }, arguments[1] || {});
		
		this.wrapper.innerHTML = '';
		this.wrapper.appendChild(this.content);
		if (this.options.centered) {
			var tmpwidth = this.content.getWidth();
			var tmpheight = this.content.getHeight();
			
			var topmargin = tmpheight / 2;
			topmargin = '-'+topmargin+'px';
			var leftmargin = tmpwidth / 2;
			leftmargin = '-'+leftmargin+'px';
			
			this.wrapper.setStyle({position: 'absolute', top: '50%', left: '50%', marginLeft: leftmargin, marginTop: topmargin, zIndex: '200', width: tmpwidth+'px', height: tmpheight+'px', display: 'none' });
		}
		this.content.show();
	},
	close: function()
	{
		Element.hide(this.wrapper);
	},
	open: function()
	{
		Element.show(this.wrapper);
	}
});


// switchTab
// Easily switch between tabs without complicated setup process

/* Tab links need to be outside the tab and should <a> elements with class 'tab' and a unique ID. These links should call switchTab with their own ID as the argument.
 * Tab divs should have the class 'tabdiv' and use the link's ID + 'Div' i.e. link is 'friendsTab' so the div's ID is 'friendsTabDiv'.
 */
function switchTab(tabID, containerID) {
	if (typeof tabID == 'string') {
		// old-style...
		var switchtab = $(tabID + 'Div');
		var switchto = $(tabID);
		var tabs = $(containerID).getElementsBySelector('li a');
		for(var i = 0; i < tabs.length; i++) {
			var current = tabs[i];
			current.removeClassName('active');
			var curid = current.id.toString();
			$(curid + 'Div').hide();
		}
		
		switchto.addClassName('active');
		switchtab.show();
		
		if (switchtab.down('.autofocus')) {
			switchtab.down('.autofocus').focus();
		}
	}
	else {
		// new-style, trying with mbo
		// tabID = clicked tab
		// containerID = css selector for associated div, relative to tablist
		var tab = $(tabID);
		var container = tab.up('.tablist');
		$A(container.getElementsBySelector('.tabs li a')).invoke('removeClassName', 'active');
		tab.addClassName('active');
		$A(container.getElementsByClassName('tabDiv')).invoke('hide');
		$A(container.getElementsBySelector(containerID)).invoke('show');
	}
}

// similar to above, but adds them as a single collection that applies the events to any existing events.  So specific actions can be called locally 
// to the tab, and the the visual aspect is attached aftward, not inline.
function cssTabSet(tset) {
	//call back to clear tabs
	var clearTabs = function(c){
		$A($(c).getElementsByTagName('a')).each(function(el){
			if(el.hasClassName('active')){
				el.removeClassName('active');
			}
		});
	};
	
	//main loop that adds the events that call the callback and set the class to active
	$A($(tset).getElementsByTagName('a')).each(function(t) {
		if(t.id!="ibNew") {
			Event.observe(t,'click', function() { clearCSSTabs(tset); t.addClassName('active'); }, false);
		}
	});
}

function clearCSSTabs(ele) {
	$(ele).select('a.active').each( function(el) { el.removeClassName('active'); } );
}

function activateCSSTab(ele,tab) {
	if($(tab).id!="ibNew") {
		clearCSSTabs(ele);
		if($(tab)) {
			$(tab).addClassName('active');
		}
	}
}

// message box
function internalLink(divName, pathName) {
	new Ajax.Updater(divName, pathName, {evalScripts:true, method: 'get'});
}

function internalLinkAndUnread(divName, pathName, id) {
	$('message_row_'+id).removeClassName("unread");
	new Ajax.Updater(divName, pathName, {evalScripts:true, method: 'get'});
}

function refreshInbox(divName) {
	if ($('messagesNavigation')) {
		new Ajax.Updater(divName, '/messages/inbox', {evalScripts:true, method: 'get'});
		activateCSSTab('messagesNavigation','ibAll');
	} 
}

String.prototype.truncate = function(width, widthPerChar) {
	var text = String(this);
	if (typeof widthPerChar != 'number') {
		widthPerChar = 10;
	}
	
	// Calculate max # of characters based on size and width
	var maxLength = (width / (widthPerChar * 0.5)) - 1;
	var stuff = text;
	
	// Check length
	if (stuff.length > maxLength) {
		var newstuff = '';
		var stop = false;
		// Split at words so we can avoid breaking in the middle of a word if possible
		var words = stuff.split(/ /g);
		for (var i = 0; i < words.length; i++) {
			if (!stop) {
				var word = words[i];
				if (i == 0) {
					newstuff += word;
				} else {
					if (newstuff.length + word.length + 1 <= (maxLength)) {
						newstuff += ' ' + word;
					} else {
						if (maxLength - newstuff.length > 4) {
							var diff = maxLength - newstuff.length - 1;
							newstuff += ' ' + word.slice(0,diff);
						} else {
							stop = true;
						}
					}
				};
			}
		};
		newstuff += '...';
		return newstuff;
	} else {
		return stuff;
	}
};

// display user profile in main panels
function showProfile(user) {	
	GG.siteUI.popup.viewProfile(user);
	//PanelSet.addMainPanel('profileWindow', '/account/info/'+user);	
}

// update selected party info
function updateSelectedParty(id) {
	//alert("foo");
	new Ajax.Updater('selected_party_info','/gameplay/updateselectedparty/',{parameters: 'party_id='+id});
}

// dynamically load CSS files for game specific styles
var gameStyleSheets = [];
function appendStyleSheet(game, ie6) {
	var gameID = game;
	var stylePath = "/manifest/"+game+"/css/main.css";
	if(ie6)	{
		gameID = game+"_ie6";
		stylePath = "/manifest/"+game+"/css/main_ie6.css";
	}
	if(!gameStyleSheets[gameID]){
		var gameCSS=document.createElement("link");
		gameCSS.setAttribute("rel", "stylesheet");
		gameCSS.setAttribute("type", "text/css");
		gameCSS.setAttribute("href", stylePath);
		if (typeof gameCSS!="undefined") {
			document.getElementsByTagName("head")[0].appendChild(gameCSS);
		}
		gameStyleSheets[gameID] = true;
	}
}

function toggleInboxCheckAll(ele, container) {
	$A($(container).getElementsByTagName('input')).each(function(t){
		t.checked = ele.checked;
	});
}

function inboxCheckAll(ele, container) {
	$A($(container).getElementsByTagName('input')).each(function(t){
		t.checked = true;
	});
}

function inboxUnCheckAll(ele, container) {
	$A($(container).getElementsByTagName('input')).each(function(t){
		t.checked = false;
	});
}

function setInstructionText(cont) {
	$('inGameInstructions').innerHTML = cont;
}


function setGamePageDisplay(gameState) {
	$('gameState').className = gameState;
}

function showInstructionText() {
	//todo: move each of these if blocks into <$g.surname>.js so this function can be generic, and new games could be added easier
	if(GG.game.base.game.surname == "Zap") {
		setInstructionText("<span class='zap'>Press [ESC] to Pause - Press [ESC] then select 'Video: Switch to Fullscreen' to go to Full Screen - Press [g] to Chat</span>");
	}
	else if(GG.game.base.game.surname == "AceOfAces") {
		setInstructionText("<span class='aceofaces'>Press [ESC] to Pause - Press [Alt]-[Enter] to go to Full Screen - Press [Enter] to speak to everyone - Press [T] to speak to your team</span>");
	}
	else if(GG.game.base.game.surname == "Rokkitball") {
		setInstructionText("<span class='rokkitball'>Press [ESC] to Pause - Press [t] to speak to everyone - Press [y] to speak to your team</span>");
	}
	else if(GG.game.base.game.surname == "MarbleBlast") {
		setInstructionText("<span class='marbleblast'>Press [ESC] to Pause - Press [t] to Chat</span>");
	}
	else if(GG.game.base.game.surname == "Legions") {
		setInstructionText("<span class='legions'>Press [ESC] to Pause - Press [Alt]-[Enter] to go to Full Screen - Press [t] to speak to everyone - Press [y] to speak to your team</span>");
	}
	else if(GG.game.base.game.surname == "Lore") {
		setInstructionText("<span class='lore'>Press [ESC] to Pause - Press [Alt]-[Enter] to go to Full Screen - Press [y] to speak to everyone - Press [t] to speak to your team</span>");
	}
	else if(GG.game.base.game.surname == "Galcon") {
		setInstructionText("<span class='galcon'>Press [ESC] to Pause - Type to Chat</span>");
	}
	else if(GG.game.base.game.surname == "Racer") {
		setInstructionText("<span class='racer'>Press [ESC] to Pause</span>");
	}
	else {
		setInstructionText("<span class='default'>Press [ESC] to Pause - Press [Enter] to Chat</span>");
	}
	Element.hide('footerContents');
	Element.show('inGameInstructions');
}

function hideInstructionText() {
	Element.show('footerContents');
	Element.hide('inGameInstructions');
}

// Cool forEach function that will work on objects or arrays

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
    Array.forEach = function(object, block, context) {
        for (var i = 0; i < object.length; i++) {
            block.call(context, object[i], i, object);
        }
    };
}

// generic enumeration
Function.prototype.forEach = function(object, block, context) {
    for (var key in object) {
        if (typeof this.prototype[key] == "undefined") {
            block.call(context, object[key], key, object);
        }
    }
};

// globally resolve forEach enumeration
var forEach = function(object, block, context) {
    if (object) {
        var resolve = Object; // default
        if (object instanceof Function) {
            // functions have a "length" property
            resolve = Function;
        } else if (object.forEach instanceof Function) {
            // the object implements a custom forEach method so use that
            object.forEach(block, context);
            return;
        } else if (typeof object.length == "number") {
            // the object is array-like
            resolve = Array;
        }
        resolve.forEach(object, block, context);
    }
};

// image map roll over tool
function addRollStates(id,obj,imageBkg) {
	obj = $(obj);
	Event.observe($(id), 'mouseout', function(){ if (obj) { obj.style.backgroundImage = ""; } });
	Event.observe($(id), 'mouseover', function(){ if (obj) { obj.style.backgroundImage = "url(/images/"+imageBkg+".jpg)"; }});
}


// utility function for contact imports
function checkAll() {
	var inp = document.getElementsByTagName('input');
	var chk = new Array();
	for(var i=0;i<inp.length;i++){
		if(inp[i].getAttribute('type')=='checkbox' && inp[i].id != '' ) {
			//alert('name: ' + inp[i].name + ' checked: ' + inp[i].checked);
			if (inp[i].checked == false) inp[i].checked = true;
		}
	}
}

// utility function for contact imports
function uncheckAll() {
	var inp = document.getElementsByTagName('input');
	var chk = new Array();
	for(var i=0;i<inp.length;i++){
		if(inp[i].getAttribute('type')=='checkbox' && inp[i].id != '' ) {
			//alert('name: ' + inp[i].name + ' checked: ' + inp[i].checked);
			//alert('id: ' + inp[i].id + ' checked: ' + inp[i].checked);
			if (inp[i].checked == true) inp[i].checked = false;
		}
	}
}

function convertDate(el) {
	var timestamp = parseInt(el.innerHTML);
	// If the timestamp is 0, show that there is no activity
	if(timestamp == 0) {
		el.innerHTML = '<span style="display: none;">'+timestamp+'</span> N/A';
		return;
	}
	
	var d = new Date(timestamp*1000);
	var today = new Date();
	el.innerHTML = '<span style="display: none;">'+timestamp+'</span><acronym title="'+fullDate(d,1)+'">'+relativeDate(d)+'</acronym>';
}

// In milliseconds:
var minute = 60*1000;
var hour = minute*60;
var day = hour*24;
var week = day*7;
var month = day*30;
var year = day*365;

function relativeDate(timestamp, dateStyle) {
	if (typeof dateStyle == 'undefined' || dateStyle == null) {
		dateStyle = 1;
	}
	var now = new Date();
	var timezone = now.getTimezoneOffset() * 60 * 1000;
	var date = new Date((timestamp * 1000) - timezone);
	var diff = now.getTime() - date.getTime();
	
	if(dateStyle == 1)
	{
		if(diff < 10*minute) return 'just now';
		if(diff < 1*day && date.getDate() == now.getDate()) return 'today';
		if(diff < 2*day && date.getDate() == now.getDate()-1) return 'yesterday';
		if(diff < 1*week && date.getDay() < now.getDay()) return 'this week';
		if(diff < (13-now.getDay())*day) return 'last week';
		if(diff < 1*month && date.getMonth() == now.getMonth()) return 'this month';
		if(diff < 2*month && date.getMonth() == now.getMonth()-1) return 'last month';
		
		// None of the above
		return 'a long time ago';
	}
	else if(dateStyle == 3)
	{
		return fullDate(date);
	}
	else
	{
		var s = '-';
		if(diff > year)
		{
			s += parseInt(diff/year)+'Y ';
			diff = diff - (parseInt(diff/year) * year);
		}
		if(diff > month)
		{
			s += parseInt(diff/month)+'M ';
			diff = diff - (parseInt(diff/month) * month);
		}
		if(diff > day)
		{
			s += parseInt(diff/day)+'D ';
			diff = diff - (parseInt(diff/day) * day);
		}
		if(diff > hour)
		{
			s += parseInt(diff/hour)+'H ';
			diff = diff - (parseInt(diff/hour) * hour);
		}
		if(diff > minute)
		{
			s += parseInt(diff/minute)+'m ';
			diff = diff - (parseInt(diff/minute) * minute);
		}
		else
		{
			s += '1m';
		}
		
		return s;
	}
}

function toggleTokenDesc(offLabel, onLabel) {
	if ($('earnTokensDesc').style.display == "none") {
		new Effect.SlideDown('earnTokensDesc',{duration:0.2});
		$('toggleActionLabel').innerHTML = onLabel;
	}
	else {
		new Effect.SlideUp('earnTokensDesc',{duration:0.2});
		$('toggleActionLabel').innerHTML = offLabel;
	}
}

function fullDate(dateObject, withDayOfWeek) {
	var d = dateObject;
	var month_names = new Array ( );
	month_names[month_names.length] = "Janurary";
	month_names[month_names.length] = "February";
	month_names[month_names.length] = "March";
	month_names[month_names.length] = "April";
	month_names[month_names.length] = "May";
	month_names[month_names.length] = "June";
	month_names[month_names.length] = "July";
	month_names[month_names.length] = "August";
	month_names[month_names.length] = "September";
	month_names[month_names.length] = "October";
	month_names[month_names.length] = "November";
	month_names[month_names.length] = "December";
	
	var day_names = new Array ( );
	day_names[day_names.length] = "Sunday";
	day_names[day_names.length] = "Monday";
	day_names[day_names.length] = "Tuesday";
	day_names[day_names.length] = "Wednesday";
	day_names[day_names.length] = "Thursday";
	day_names[day_names.length] = "Friday";
	day_names[day_names.length] = "Saturday";
	
	var date = '';
	
	var hours = d.getHours();
	var amp = '';
	if(hours >= 12)
	{
		hours = hours == 12 ? 12 : hours - 12;
		amp = 'PM';
	}
	else
	{
		amp = 'AM';
		hours = hours == 0 ? 12 : hours;
	}
	
	if(withDayOfWeek == 1)
	{
		date += day_names[d.getDay()];
		date += " ";
	}
	date += month_names[d.getMonth()];
	date += " " + d.getDate();
	date += ", ";
	date += " " + d.getFullYear();
	date += " ";
	date += hours;
	date += ":";
	date += d.getMinutes() < 10 ? '0'+d.getMinutes() : d.getMinutes();
	date += " ";
	date += amp;
	
	return date;
}

function copyToClipBoard(inElement) {
	inElement = $(inElement);
	 if (inElement.createTextRange) {
		//IE
		var range = inElement.createTextRange();
		if (range) {
			range.execCommand('Copy');
		}
	  } else {
		//FF
		var flashcopier = 'flashcopier';
		if(!$(flashcopier)) {
			var divholder = document.createElement('div');
			divholder.id = flashcopier;
			document.body.appendChild(divholder);
		}
		$(flashcopier).innerHTML = '';
		var divinfo = '<embed src="/swf/_clipboard.swf" FlashVars="clipboard='+encodeURIComponent(inElement.value)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>';
		$(flashcopier).innerHTML = divinfo;
	}
}
