if ( window.loadFirebugConsole && !window.console )
	window.loadFirebugConsole();

// Prototype (et al) adjustments
( function ( ) {

	// Array
	if ( Array.prototype.shuffle === undefined ) {
		Array.prototype.shuffle = function ( ) {
			return this.sort ( function ( ) {
				return 0.5 - Math.random();
			} );
		}
	}
	if ( Array.prototype.random === undefined ) {
		Array.prototype.random = function ( count ) {
			if ( !this.length ) return null;
			var c = Math.max ( Math.min ( this.length, parseInt ( count ) || 1 ), 0 );
			var r = this.shuffle().slice(0,c);
			if ( count == null ) return r[0];
			return r;
		}
	}

	// Math
	if ( Math.randomInt === undefined ) {
		Math.randomInt = function ( min, max ) {
			if ( min > max ) {
				var tmp = min;
				min = max;
				max = tmp;
			}
			return Math.floor ( Math.random() * ( max - min + 1 ) + min );
		}
	}
	if ( Math.ln === undefined ) {
		Math.ln = Math.log;
		Math.log = function ( x, b ) {
			if ( b == null ) b = Math.E;
			return Math.ln ( x ) / Math.ln ( b );
		}
	}

	// String
	if ( String.prototype.empty === undefined ) {
		String.prototype.empty = function ( ) { return this.length == 0; }
	}
	if ( String.prototype.toHref === undefined ) {
		String.prototype.toHref = function ( ) {
			var string = this;
			var f = function ( x ) {
				if ( x ) string = string.substring ( x.length );
				return x;
			}
			var l = document.location;
			var o = {};
			o.protocol = ( s = f ( ( s = string.match(/^([a-z]+:\/\/).+/i) ) ? s[1] : null ) ) ? s.replace(/\/\/$/,'') : null;
			o.host = f ( ( s = (o.protocol+"//"+string).match(/^[a-z\.]+:\/\/([\w\.:]+)(\/|$)/i) ) ? s[1] : null );
			o.hostname = ( s = ( o.host || "" ).match(/^([^:]+)/) ) ? s[1] : null;
			o.port = ( s = ( s = ( o.host || "" ).match(/^[^:]+(:\d+)?$/) ) ? s[1] : ( o.hostname ? 80 : null ) ) ? s.replace(/^:/,'') : null;
			o.pathname = f ( ( s = string.match(/^([^#]*)/) ) ? s[1] : null );
			o.hash = f ( ( s = string.match(/(#[^\?]*)(\?|$)/) ) ? s[1] : "" );
			o.search = f ( ( s = string.match(/(\?.*)$/) ) ? s[1] : "" );
			if ( o.pathname.charAt(0) != "/" ) {
				if ( o.host == l.host ) {
					o.pathname = l.pathname.replace(/\/[^\/]*$/,'/') + o.pathname;
				} else {
					o.pathname = "/" + o.pathname;
				}
			}
			for ( p in o ) { if ( o[p] === null ) o[p] = l[p]; }
			o.href = o.protocol + "//" + o.host + o.pathname + o.hash + o.search;
			o.internal = !!( ( o.protocol + "//" + o.host + o.pathname == l.protocol + "//" + l.host + l.pathname ) && o.hash );
			return o;
		}
	}
	if ( String.prototype.stripTags === undefined ) {
		String.prototype.stripTags = function ( ) {
			return this.replace ( /<[^>]*>/g, '' );
		}
	}
	if ( String.prototype.lpad === undefined ) {
		String.prototype.lpad = function ( length, chr ) {
			var s = this;
			chr = ( chr || "" ).toString().charAt(0) || " ";
			while ( s.length < length ) {
				s = chr + s;
			}
			return s;
		}
	}
	if ( String.prototype.rpad === undefined ) {
		String.prototype.rpad = function ( length, chr ) {
			var s = this;
			chr = ( chr || "" ).toString().charAt(0) || " ";
			while ( s.length < length ) {
				s += chr;
			}
			return s;
		}
	}
	if ( String.prototype.ltrim === undefined ) {
		String.prototype.ltrim = function ( ) {
			return this.replace ( /^\s*/, "" );
		}
	}
	if ( String.prototype.rtrim === undefined ) {
		String.prototype.rtrim = function ( ) {
			return this.replace ( /\s*$/, "" );
		}
	}
	if ( String.prototype.trim === undefined ) {
		String.prototype.trim = function ( ) {
			return this.ltrim().rtrim();
		}
	}
	if ( String.prototype.ucwords === undefined ) {
		String.prototype.ucwords = function ( ) {
			return this.replace ( /(^|\s)\w/g, function ( s ) { return s.toUpperCase(); } );
		}
	}
	String.prototype.charAt = ( function ( ) {
		var charAt = String.prototype.charAt;
		return function ( pos ) {
			if ( parseInt ( pos ) >= 0 ) return charAt.call ( this, pos );
			pos = parseInt ( pos );
			if ( isNaN ( pos ) ) return "";
			return this.substr ( this.length + pos, 1 );
		}
	} )();

	// Date
	if ( Date.days === undefined ) {
		Date.days = [
			{
				abbr : "Mon",
				standard : "Monday"
			},
			{
				abbr : "Tue",
				standard : "Tuesday"
			},
			{
				abbr : "Wed",
				standard : "Wednesday"
			},
			{
				abbr : "Thu",
				standard : "Thursday"
			},
			{
				abbr : "Fri",
				standard : "Friday"
			},
			{
				abbr : "Sat",
				standard : "Saturday"
			},
			{
				abbr : "Sun",
				standard : "Sunday"
			}
		];
	}
	if ( Date.months === undefined ) {
		Date.months = [
			{
				abbr : "Jan",
				standard : "January"
			},
			{
				abbr : "Feb",
				standard : "February"
			},
			{
				abbr : "Mar",
				standard : "March"
			},
			{
				abbr : "Apr",
				standard : "April"
			},
			{
				abbr : "May",
				standard : "May"
			},
			{
				abbr : "Jun",
				standard : "June"
			},
			{
				abbr : "Jul",
				standard : "July"
			},
			{
				abbr : "Aug",
				standard : "August"
			},
			{
				abbr : "Sep",
				standard : "September"
			},
			{
				abbr : "Oct",
				standard : "October"
			},
			{
				abbr : "Nov",
				standard : "November"
			},
			{
				abbr : "Dec",
				standard : "December"
			}
		]
	}
	if ( Date.suffixes === undefined ) {
		Date.suffixes = [ "st", "nd", "rd", "th" ];
	}
	if ( Date.now === undefined ) {
		Date.now = function ( ) {
			return new Date().getTime();
		}
	}
	if ( Date.fromUTC === undefined ) {
		Date.fromUTC = ( function ( ) {
			var p = function ( n ) {
				return parseInt ( (""+n).replace(/^0*(.*)$/,'$1') || 0 );
			}
			return function ( ) {
				if ( arguments.length == 1 && arguments[0] instanceof Date ) {
					var d = arguments[0];
					return arguments.callee (
						d.getFullYear(), d.getMonth()-1, d.getDate(),
						d.getHours(), d.getMinutes(), d.getSeconds()
					);
				} else if ( arguments.length == 1 && typeof arguments[0] == "string" ) {
					var tsData = arguments[0].split(/[- :T]/);
					if ( tsData.length == 6 ) {
						tsData[1] = p(tsData[1]) - 1;
						return arguments.callee.apply ( null, tsData );
					} else {
						return null;
					}
				} else {
					var d = new Date();
					var n;
					var params = [ "FullYear", "Month", "Date", "Hours", "Minutes", "Seconds" ];
					for ( var i = 0; i < params.length; i ++ ) {
						if ( !isNaN(n=p(arguments[i]) ) )
							d["setUTC"+params[i]](n);
					}
					return d;
				}
			}
		} )();
	}
	if ( Date.create === undefined ) {
		Date.create = ( function ( ) {
			var p = function ( n ) {
				return parseInt ( (""+n).replace(/^0*(.*)$/,'$1') || 0 );
			}
			return function ( ) {
				var a = arguments;
				return new Date ( p(a[0]), p(a[1]), p(a[2]), p(a[3]), p(a[4]), p(a[5]) );
			}
		} )();
	}
	if ( Date.prototype.copy === undefined ) {
		Date.prototype.copy = function ( ) {
			return new Date ( this.getTime() );
		}
	}
	if ( Date.prototype.getISOYear === undefined ) {
		Date.prototype.getISOYear = function ( ) {
			var weekNum = this.getWeekOfYear();
			return ( weekNum > 4 && this.getMonth() == 0 ) ? this.getFullYear() - 1 : this.getFullYear();
		}
	}
	if ( Date.prototype.getTimezoneOffsetHours === undefined ) {
		Date.prototype.getTimezoneOffsetHours = function ( ) {
			return parseInt ( this.getTimezoneOffset() / 60 );
		}
	}
	if ( Date.prototype.getTimezoneOffsetMinutes === undefined ) {
		Date.prototype.getTimezoneOffsetMinutes = function ( ) {
			var o = this.getTimezoneOffset();
			return o - parseInt ( o / 60 ) * 60;
		}
	}
	if ( Date.prototype.getAmPm === undefined ) {
		Date.prototype.getAmPm = function ( ) {
			return ( this.getHours() < 12 ) ? "AM" : "PM";
		}
	}
	if ( Date.prototype.getDayNameAbbr === undefined ) {
		Date.prototype.getDayNameAbbr = function ( ) {
			var d = ( ( this.getDay() + 6 ) % 7 );
			return Date.days[d].abbr || Date.days[d].standard.substring(0,3);;
		}
	}
	if ( Date.prototype.getDayName === undefined ) {
		Date.prototype.getDayName = function ( ) {
			var d = ( ( this.getDay() + 6 ) % 7 );
			return Date.days[d].standard;
		}
	}
	if ( Date.prototype.getMonthNameAbbr === undefined ) {
		Date.prototype.getMonthNameAbbr = function ( ) {
			return Date.months[this.getMonth()].abbr || Date.months[this.getMonth()].standard.substring(0,3);;
		}
	}
	if ( Date.prototype.getMonthName === undefined ) {
		Date.prototype.getMonthName = function ( ) {
			return Date.months[this.getMonth()].standard;
		}
	}
	if ( Date.prototype.getSuffix === undefined ) {
		Date.prototype.getSuffix = function ( ) {
			var l1 = parseInt((""+this.getDate()).charAt(-1));
			var l2 = parseInt((""+this.getDate()).replace(/^.*(\d\d)$/,'$1'));
			return Date.suffixes[(l2-10==l1)?3:Math.min(l1,4)-1];
		}
	}
	if ( Date.prototype.getDifference === undefined ) {
		Date.prototype.getDifference = function ( d ) {
			d = d || new Date();
			var difference = d.getTime() - this.getTime();
			switch ( ( arguments[1] || "" ).toString().toLowerCase() ) {
				case "days":
				case "d":
					return Math.ceil ( difference / ( 1000 * 60 * 60 * 24 ) );
					break;
				case "hours":
				case "h":
					return Math.ceil ( difference / ( 1000 * 60 * 60 ) );
					break;
				case "minutes":
				case "mins":
				case "m":
					return Math.ceil ( difference / ( 1000 * 60 ) );
					break;
				case "seconds":
				case "secs":
				case "s":
					return Math.ceil ( difference / ( 1000 ) );
					break;
				default:
					return difference;
			}
		}
	}
	if ( Date.prototype.getDaysUntil === undefined ) {
		Date.prototype.getDaysUntil = function ( d ) {
			d = ( d || new Date() ).copy();
			d.setHours(this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds());
			var diff = d.getTime() - this.getTime();
			return Math.round ( diff / ( 1000 * 60 * 60 * 24 ) );
		};
	}
	if ( Date.prototype.getDayOfYear === undefined ) {
		Date.prototype.getDayOfYear = function ( ) {
			return this.getDaysUntil ( new Date ( this.getFullYear(), 0, 1 ) ) * -1;
		};
	}
	if ( Date.prototype.getWeekOfYear === undefined ) {
		Date.prototype.getWeekOfYear = function ( ) {
			var d = new Date ( this.getFullYear(), 0, 1 );
			d.setDate ( d.getDate() + ( 8 - d.getDay() ) );
			var days = this.getDaysUntil ( d ) * -1;
			var week = Math.floor ( days / 7 );
			return week < 0 ? 53 + week : week;
		}
	}
	if ( Date.prototype.getDaysInMonth === undefined ) {
		Date.prototype.getDaysInMonth = function ( ) {
			return new Date ( this.getFullYear(), this.getMonth() + 1, 0 ).getDate();
		};
	}
	if ( Date.prototype.isLeapYear === undefined ) {
		Date.prototype.isLeapYear = function ( ) {
			return ( new Date ( this.getFullYear(), 1, 1 ).getDaysInMonth() ) == 29;
		}
	}
	Date.prototype.toString = ( function ( ) {
		var toString = Date.prototype.toString;
		return function ( ) {
			if ( !arguments.length ) return toString.call ( this );
			if ( arguments.length > 1 ) {
				var a = [];
				for ( var i = 0; i < arguments.length; i ++ ) {
					a.push ( this.toString ( arguments[i] ) );
				}
				return a;
			}
			var input = arguments[0].toString();
			var date = this;
			var output = input.replace ( /%(.)/g, function ( ) {
				switch ( arguments[1] ) {
					// Day
					case "d": // Day of the month, 2 digits with leading zeros
						return (""+date.getDate()).lpad(2,"0"); break;
					case "D": // A textual representation of a day, three letters
						return date.getDayNameAbbr();
					case "j": // Day of the month without leading zeros
						return date.getDate(); break;
					case "l": // A full textual representation of the day of the week
						return date.getDayName(); break;
					case "N": // ISO-8601 numeric representation of the day of the week
						return ( ( ( date.getDay() + 6 ) % 7 ) + 1 ); break;
					case "S": // Ordinal suffix for the day of the month, 2 characters
						return date.getSuffix(); break;
					case "w": // Numeric representation of the day of the week
						return date.getDay(); break;
					case "z": // The day of the year (starting from 0)
						return date.getDayOfYear(); break;

					// Week
					case "W": // ISO-8601 week number of year, weeks starting on Monday
						return date.getWeekOfYear(); break;

					// Month
					case "F": // A full textual representation of the month
						return date.getMonthName(); break;
					case "m": // Numeric representation of the month, with leading zeros
						return ("0"+(date.getMonth()+1)).replace(/.*(\d\d)$/,'$1'); break;
					case "M": // A short textual representation of the month, three letters
						return date.getMonthNameAbbr(); break;
					case "n": // Numeric representation of the month, without leading zeros
						return date.getMonth()+1; break;
					case "t": // Number of days in the given month
						return date.getDaysInMonth(); break;

					// Year
					case "L": // Whether it's a leap year
						return date.isLeapYear() ? 1 : 0; break;
					case "o": // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.
						return date.getISOYear(); break;
					case "Y": // A full numeric representation of the year, 4 digits
						return date.getFullYear(); break;
					case "y": // A two digit representation of the year
						return (""+date.getFullYear()).replace(/^.*(\d\d)$/,'$1'); break;

					// Time
					case "a": // Lowercase Ante meridiem and Post meridiem
						return date.getAmPm().toLowerCase(); break;
					case "A": // Uppercase Ante meridiem and Post meridiem
						return date.getAmPm();
					case "B": // Swatch Internet time
						return "B"; break; // not implemented
					case "g": // 12-hour format of the hour without leading zeros
						return ( ( ( ( date.getHours() % 12 ) + 11 ) % 12 ) + 1 ); break;
					case "G": // 24-hour format of the hour without leading zeros
						return date.getHours(); break;
					case "h": // 12-hour format of the hour with leading zeros
						return (""+(date.getHours()%12)).lpad(2,"0"); break;
					case "H": // 24-hour format of the hour with leading zeros
						return (""+date.getHours()).lpad(2,"0"); break;
					case "i": // Minutes with leading zeros
						return (""+date.getMinutes()).lpad(2,"0"); break;
					case "s": // Seconds, with leading zeros
						return (""+date.getSeconds()).lpad(2,"0"); break;
					case "u": // Milliseconds
						return date.getMilliseconds(); break;

					// Timezone
					case "e": // Timezone identifier
						return "e"; break; // not implemented
					case "I": // Whether or not the date is in daylight saving time
						return "I"; break; // not implemented
					case "P": // Difference to Greenwich time (GMT) with colon between hours and minutes
						var seperator = ":"; // Yes, 'P' should follow 'O', but
											 // it's more efficient this way!
					case "O": // Difference to Greenwich time (GMT) in hours
						var seperator = seperator || "";
						var to = date.getTimezoneOffset() * -1;
						var h = parseInt ( Math.abs ( to / 60 ) );
						var m = parseInt ( Math.abs ( to ) - h * 60 );
						return ( to < 0 ? "-" : "+" ) + (""+h).lpad(2,"0") + seperator + (""+m).lpad(2,"0"); break;
					case "T": // Timezone abbreviation
						return "T"; break; // not implemented
					case "Z": // Timezone offset in seconds.
						return date.getTimezoneOffset() * 60; break;

					// Full Date/Time
					case "c": // ISO 8601 date
						return date.toString("%Y-%m-%dT%H:%i:%s%P"); break;
					case "r": // RFC 2822 formatted date
						return date.toString("%D, %j %M %Y %H:%i:%s %O"); break;
					case "U": // Seconds since the Unix Epoch
						return parseInt ( date.getTime() / 1000 ); break;

					default:
						return arguments[1];
				}
			} );
			return output;
		}
	} )();
} )();

( function ( ) {

	var is = function ( type ) {
		var m = "[object " + type.toLowerCase().ucwords() + "]";
		return function ( o ) {
			return Object.prototype.toString.call ( o ) == m;
		}
	}

	for ( var i = 0; i < arguments.length; i ++ )
		window["is" + arguments[i].toLowerCase().ucwords()] = is ( arguments[i] );

} ) ( "object", "array", "date" );

var cookies = ( function ( ) {

	var getCookie = function ( key ) {
		var cookies = document.cookie.split(";");
		var name = key + "=";
		for ( var i = 0; i < cookies.length; i ++ ) {
			if ( cookies[i].ltrim().indexOf(name) == 0 ) {
				return cookies[i].ltrim().substring(name.length);
			}
		}
		return null;
	}

	var setCookie = function ( key, value, expires, path ) {
		if ( expires ) {
			if ( isDate ( expires ) ) {
				expires = " expires=" + expires.toGMTString();
			} else if ( !isNaN ( parseInt ( expires ) ) ) {
				var d = new Date();
				d.setTime ( d.getTime() + ( parseInt ( expires ) * 24 * 60 * 60 * 1000 ) );
				expires = "; expires=" + d.toGMTString();
			} else {
				expires = ";";
			}
		} else {
			expires = ";";
		}
		if ( path ) {
			path = "; path=" + ( path || "/" ) + ";";
		}
		document.cookie = key + "=" + value + expires + path;
	}

	var $ = {
		get : function ( key, def ) {
			var c = getCookie ( key );
			return c || def;
		},
		set : function ( key, value, expires, path ) {
			var o = getCookie ( key );
			setCookie ( key, value, expires, path );
			return o;
		},
		remove : function ( key ) {
			return $.set ( key, "", -1 );
		}
	}

	return $;

} )();

function goToBottom ( ) {
	$('#body')[0].scrollIntoView(false);
}

var fixFloatingBars = ( function ( ) {
	var timeout;
	return function ( ) {
		var fn = function ( ) {
			$('#bottomWrapper').css ( 'width', $('#body').outerWidth() );
			$('#topWrapper').css ( 'width', $('#body').outerWidth() );
		}
		clearTimeout ( timeout );
		timeout = setTimeout ( fn, 50 );
		fn();
	}
} )();

var hideBottom = function ( time, callback ) {
	var cb = function ( ) { if ( typeof callback == "function" ) callback(); }
	if ( isNaN ( parseInt ( time ) ) ) time = false;
	if ( $('#taskbar').length ) {
		var height = $('#taskbar').css({position:'',height:''}).outerHeight();
		$('#taskbar').css ( {
			height : height,
			position: "absolute",
			top: 0,
			left: 0,
			width: '100%'
		} );
		$('#bottom').height ( height );
	}
	if ( !time ) {
		$('#bottom').css ( { height : 0 } );
		$('#body').css ( { paddingBottom : 0 } );
		fixFloatingBars();
		cb();
	} else {
		$('#bottom').animate ( { height : 0 }, {
			duration : parseInt(time),
			step : function () { this.style.overflow = ''; },
			complete : cb
		} );
		$('#body').animate( { paddingBottom : 0 }, {
			duration : parseInt ( time ),
			step : fixFloatingBars,
			complete : fixFloatingBars
		} );
	}
}

var showBottom = function ( time, callback ) {
	var cb = function ( ) { if ( typeof callback == "function" ) callback(); }
	if ( isNaN ( parseInt ( time ) ) ) time = false;
	var height = parseInt ( $('#bottom').css('height','').outerHeight() );
	if ( $('#taskbar').length ) {
		height = parseInt ( $('#taskbar').css({position:'',height:''}).outerHeight() );
		$('#taskbar').css ( {
			height : height,
			position: "absolute",
			top: 0,
			left: 0,
			width: '100%'
		} );
	}
	var scrollOffset = ( 100 / ( $('#body').outerHeight() - $('#middle').outerHeight() + 1 ) ) * -$('#body').offset().top / 100;
	if ( !time ) {
		$('#bottom').css ( { height : height } );
		$('#body').css ( { paddingBottom : height + 25 } );
		$('#middle').css ( { scrollTop : '+=' + Math.round(scrollOffset*(height+25)) } );
		fixFloatingBars();
		cb();
	} else {
		$('#bottom').css('height',0).animate( { height : height }, {
			duration : parseInt ( time ),
			step : function ( ) { this.style.overflow = ''; },
			complete : cb
		} );
		$('#body').animate ( { paddingBottom : height + 25 }, {
			duration : parseInt ( time ),
			step : fixFloatingBars,
			complete : fixFloatingBars
		} );
		$('#middle').animate ( { scrollTop : '+=' + Math.round(scrollOffset*(height+25)) }, {
			duration : parseInt ( time )
		} );
	}
}

var showNotification = ( function ( ) {
	var timeout;
	return function ( msg, delay ) {
		if ( !$("#notify").length ) return false;
		if ( isNaN ( parseInt ( delay ) ) ) delay = 2;
		$("#notify .notification").stop(true).animate ( { opacity : 0, bottom : "+=10" }, { duration: 100, complete : function ( ) {
			$(this).remove();
		} } );
		var bubble = $('<div class="notification"><div class="top"></div><div class="middle"></div><div class="bottom"></div></div>');
		$(".middle",bubble).text ( msg );
		bubble
			.css ( { opacity : 0, bottom : -10 } )
			.appendTo ( "#notify" )
			.animate ( { opacity : 1, bottom : 0 }, { complete : function ( ) {
				timeout = setTimeout ( function ( ) {
					bubble.animate ( { opacity : 0, bottom: 10 }, { duration : 1000, complete : function ( ) {
						bubble.remove();
					} } );
				}, parseInt ( delay ) * 1000 );
			} } );
	}
} )();

$(window)
	.resize ( fixFloatingBars )
	.ready ( function ( ) {
		setTimeout ( fixFloatingBars, 100 );
	} );
