Ikariam Enhanced UI

Enhancements for the user interface of Ikariam.

Per 09-03-2015. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name			Ikariam Enhanced UI
// @description		Enhancements for the user interface of Ikariam.
// @namespace		Tobbe
// @author			Tobbe
// @version			3.0.2
// @license			MIT License
//
// @name:de			Ikariam Enhanced UI
// @description:de	Verbesserungen der Oberfläche von Ikariam.
//
// 
// @include			http://s*.ikariam.gameforge.com/*
// 
// @exclude			http://support.*.ikariam.gameforge.com/*
// 
// @require			https://greatest.deepsurf.us/scripts/5574-ikariam-core/code/Ikariam%20Core.js?version=39332
//
// 
// @resource		de					http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/de.json
// @resource		gr					http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/gr.json
// @resource		it					http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/it.json
// @resource		lv					http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/lv.json
// @resource		ru					http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/ru.json
// @resource		core_de				http://resources.ikascripts.de/IkariamCore/v2.0/core_de.json
// @resource		core_de_settings	http://resources.ikascripts.de/IkariamCore/v2.0/core_de_settings.json
// @resource		core_gr				http://resources.ikascripts.de/IkariamCore/v2.0/core_gr.json
// @resource		core_gr_settings	http://resources.ikascripts.de/IkariamCore/v2.0/core_gr_settings.json
// @resource		core_it				http://resources.ikascripts.de/IkariamCore/v2.0/core_it.json
// @resource		core_it_settings	http://resources.ikascripts.de/IkariamCore/v2.0/core_it_settings.json
// @resource		core_lv				http://resources.ikascripts.de/IkariamCore/v2.0/core_lv.json
// @resource		core_lv_settings	http://resources.ikascripts.de/IkariamCore/v2.0/core_lv_settings.json
// @resource		core_ru				http://resources.ikascripts.de/IkariamCore/v2.0/core_ru.json
// @resource		core_ru_settings	http://resources.ikascripts.de/IkariamCore/v2.0/core_ru_settings.json
// 
// @grant			unsafeWindow
// @grant			GM_setValue
// @grant			GM_getValue
// @grant			GM_deleteValue
// @grant			GM_listValues
// @grant			GM_getResourceText
// @grant			GM_xmlhttpRequest
// 
// @bug				Opera & Chrome	Zooming with the mouse is not possible with "Shift" as access key.
// @bug				Opera & Chrome	No updating of the missing resources is possible due to a missing modification listener.
// @bug				All				The selected island is not centered in world view.
// @bug				All				If you are zooming to more than 100%, the view is not centered correctly after a page reload.
// 
// @history			3.0.2	Release: 08.03.2015
// @history			3.0.2	Bugfix:	Wrong time display for last reset of highscore information.
// @history			3.0.2	Bugfix: Start piracy raid icon was resized.
//
// @history			3.0.1	Release: 07.03.2015
// @history			3.0.1	Language: Updated Greek translation.
// @history			3.0.1	Bugfix:	Language key in message options.
// @history			3.0.1	Bugfix: Alliance member data stored at same place for all alliance members.
//
// @history			3.0		Release: 05.03.2015
// @history			3.0		Language: Russian translation added (incomplete).
// @history			3.0		Language: Italian translation added (incomplete).
// @history			3.0		Language: Greek translation added (incomplete).
// @history			3.0		Change: Implementation of Ikariam Core - rewrote the complete code.
// @history			3.0		Change: Cleared the update history to use the Ikariam Core labels.
// @history			3.0		Bugfix:	Diverse graphic errors.
// @history			3.0		Bugfix: Broken in Firefox and Chrome.
// @history			3.0		Due to rename of script: Manually deleting old script necessary.
//
// @history			2.6		Release: 12.10.2014
// @history			2.6		Bugfix: Script options were broken.
// 
// @history			2.6		Release: 19.08.2014
// @history			2.5		Bugfix: Game language is recognized again.
// @history			2.5		Bugfix: Works now in Greasemonkey 2.0+
// @history			2.5		Change: Checks now for updates on Greasy Fork.
// @history			2.5		Removed code parts for mobile version.
// @history			2.5		Removed alliance page improvement, as fixed in Ikariam v0.5.13
// 
// @history			2.6		Release: 11.09.2013
// @history			2.4		Bugfix: New Ikariam domain -> script can now be used with this domain
// 
// @history			2.3		Release: 12.04.2013
// @history			2.3		Feature: Formatting troop lists for posting in forums / personal messages.
// @history			2.3		Feature: Filling level of warehouse as bar in town view.
// @history			2.3		Feature: Link to mines / town hall when clicking on resources / citizens.
// @history			2.3		Change: New versioning system.
// @history			2.3		Bugfix: Some characters in links were not decoded correctly.
// @history			2.3		Bugfix: Watching a foreign city causes a abortion of the script.
// @history			2.3		Bugfix: The city dropdown sometimes was zoomed in world view.
// @history			2.3		Bugfix: Removed obsolete CSS styles for Firefox.
// @history			2.3		Adjustments in the language files.
// @history			2.3		Violent Monkey brings Greasemonkey functions to Opera.
// 
// @history			2.2.1	Release: 11.10.2012
// @history			2.2.1	Feature: Smaller icons in direct military tooltip.
// @history			2.2.1	Bugfix: Problems with update check and version numbers >= 10.
// @history			2.2.1	Bugfix: Problems with another script.
// @history			2.2.1	Bugfix: Problems with a wrong style in island view.
// 
// @history			2.2		Release: 10.10.2012
// @history			2.2		Feature: Message signature can be set.
// @history			2.2		Feature: Button for faster sending of circular messages.
// @history			2.2		Feature: Make links in messages clickable.
// @history			2.2		Feature: Show town information of colonizing cities.
// @history			2.2		Feature: Information about cargo / fleets is displayed directly in military view.
// @history			2.2		Feature: Script options: Sections on the script tab can be folded.
// @history			2.2		Bugfix: Tooltips with mouseover were not clickable anymore.
// @history			2.2		Changes in code for better overview.
// 
// @history			2.1.1	Release: 01.10.2012
// @history			2.1.1	Feature: Different styles for income in town view.
// @history			2.1.1	Feature: Remaining resources after upgrade.
// @history			2.1.1	Feature: Improved style for external alliance pages.
// @history			2.1.1	Feature: Refresh the missing / remaining resources in construction view automatically.
// 
// @history			2.1		Release: 23.09.2012
// @history			2.1		Feature: Show the missing resources in construction view.
// @history			2.1		Feature: Show the hourly income directly in town view and add the daily production as popup.
// @history			2.1		Feature: Don't center town information in the town advisor.
// @history			2.1		Feature: Save the highscore data of alliance members and compare it with the actual value.
// @history			2.1		Bugfix: There was an error with a missing language package and seperators.
// @history			2.1		Bugfix: Some things in worldview were not resized correctly.
// @history			2.1		Prevent more than one script execution.
// @history			2.1		Prevent more than one script option panel (the script option panel now is usable for other scripts, too).
// 
// @history			2.0.1	Release: 27.08.2012
// @history			2.0.1	Bugfix: Zooming was broken.
// @history			2.0.1	Bugfix: Dropdown menus created by the script were broken.
// @history			2.0.1	Bugfix: Tooltips in in Alliance / Military view were not shown correctly.
// 
// @history			2.0		Release: 18.07.2012
// @history			2.0		Language: Latvian translation
// @history			2.0		Feature: Cross-browser compatibility.
// @history			2.0		Feature: Possibility to hide the update hint for a new script version.
// @history			2.0		Bugfix: Resizing the owner state in world view was broken.
// @history			2.0		Some changes in the code for easier copying of functions which should be used by more than one script.
// 
// @history			1.7		Release: 12.06.2012
// @history			1.7		Feature: Resizing banners when zooming is possible in city view, too.
// @history			1.7		Feature: The zoom buttons are available in world view, too.
// @history			1.7		Feature: Zooming with the mouse scroll is possible again (now with key, standard is ctrl).
// @history			1.7		Change: Changes in the option panel due to the new zooming function features.
// @history			1.7		Bugfix: If resizing is enabled, zooming with the buttons will resize the banners, too.
// @history			1.7		Bugfix: The chat will not cause to much executions of script functions.
// @history			1.7		The language texts are integrated as resources, so that there is shorter code.
// @history			1.7		Replace the GM_* functions by myGM.* to expand them easy and add new.
// 
// @history			1.6		Release: 05.06.2012
// @history			1.6		Feature: Possibility to hide only the bird swarm animation.
// @history			1.6		Feature: Easier upkeep reduction table.
// @history			1.6		Feature: Enhanced zoom function using the Ikariam zoom function.
// @history			1.6		Due to the use of Ikariam functions the code could be reduced.
// @history			1.6		Code enhancements for shorter code.
// 
// @history			1.5.1.1	Release: 25.05.2012
// @history			1.5.1.1	Bugfix: Not all occurrences of hidden were changed.
// 
// @history			1.5.1	Release: 24.05.2012
// @history			1.5.1	Bugfix: Name of a class (hidden) is used by GF.
// 
// @history			1.5		Release: 24.05.2012
// @history			1.5		Feature: Options panel to enable/disable funtions and set settings.
// @history			1.5		Feature: Update interval can be set.
// @history			1.5		Feature: Manually check for updates.
// @history			1.5		Feature: Zoom function without resizing the whole view.
// @history			1.5		Feature: Move loading circle to another position.
// @history			1.5		Feature: Show tooltip in alliance / military view on mouseover.
// @history			1.5		Change: Code better commented. More comments, so that it is easier to understand.
// @history			1.5		Bugfix: Changed *.gif to *.png.
// @history			1.5		Version numbers adjusted.
// 
// @history			1.4.1	Release: 01.05.2012
// @history			1.4.1	Feature: Support for mobile interface.
// @history			1.4.1	Bugfix: Fixed bug with scrollbar in finances view.
// 
// @history			1.4		Release: 20.04.2012
// @history			1.4		Feature: Ready for 0.5.0, but also supports 0.4.5 furthermore.
// @history			1.4		Feature: Implemented support for different languages.
// @history			1.4		Feature: Enhanced script updater.
// @history			1.4		Change: Cleaned up code.
// @history			1.4		Change: Rename the script to "Enhanced UI".
// @history			1.4		Change: Change the namespace to "Tobbe".
// @history			1.4		Because of the change of namespace and name you have to delete the old script manually!
// 
// @history			1.3.3	Release: 11.04.2011
// @history			1.3.3	Bugfix: Problem with negative numbers and 0.4.2.4 fixed.
// 
// @history			1.3.2	Release: 15.01.2011
// @history			1.3.2	Feature: Own script updater.
// @history			1.3.2	Change: Remove everything what refered to other scripts.
// 
// @history			1.3.1	Release: 15.01.2011
// @history			1.3.1	Change: New script updater.
// 
// @history			1.3		Release: 14.01.2011
// @history			1.3		Change: Remove the script updater (Because of the problem with Greasemonkey scripts).
// 
// @history			1.2.1	Release: 28.12.2010
// @history			1.2.1	Change: New style of update panel.
// @history			1.2.1	Bugfix: Bug with ',' as seperator fixed.
// 
// @history			1.2		Release: 09.10.2010
// @history			1.2		Feature: Income in 24h added.
// @history			1.2		Change: Cleaned up code.
// 
// @history			1.1		Release: 13.04.2010
// @history			1.1		Feature: Update check implemented.
// 
// @history			1.0		Release: 12.04.2010
// @history			1.0		Initial release.
// ==/UserScript==

/**
 * Instantiate a new set of enhancement functions.
 * {@link https://greatest.deepsurf.us/scripts/4369-enhanced-ui Script on Greasy Fork}
 * {@link https://github.com/IkaScripts/IkariamEnhancedUI Script on GitHub}
 * 
 * @version	3.0.2
 * @author	Tobbe	<[email protected]>
 * 
 * @global
 * 
 * @class
 * @classdesc	Enhancements for the user interface of Ikariam.
 * 
 * @param	{IkariamCore}	$
 *   A instance of the Ikariam Core.
 */
function EnhancedUI(IC) {
	/**
	 * Script toolbar.
	 * 
	 * @type	{element}
	 */
	var ge_toolbar = IC.myGM.addElement('div', IC.myGM.$('#GF_toolbar'), { 'id': 'toolbar' }, true, IC.myGM.$('#GF_toolbar ul'));
	
	IC.myGM.addStyle(
		'#GF_toolbar > ul						{ width: auto !important; text-align: center !important; pointer-events: none; } \
		 #GF_toolbar > ul > *					{ pointer-events: auto; } \
		 #GF_toolbar #mmoNewsticker				{ display: inline-block !important; position: relative !important; } \
		 #GF_toolbar #mmoNewsticker > ul		{ width: auto !important; } \
		 #' + IC.myGM.prefix + 'toolbar			{ float: right; } \
		 #' + IC.myGM.prefix + 'toolbar > div	{ display: inline-block; position: relative; float: right; margin: 0px 5px; }',
		'scriptToolbar'
	);
	
	IC.Options.addWrapper('diverseOptions', IC.Language.$('diverse.options.wrapperTitle'));
	
	IC.con.groupCollapsed('IkariamEnhancedUI initalization ...');
	IC.con.timeStamp('IkariamEnhancedUI: toolbar and general styles created');
	
	// General functions to enhance the view.
	(function() {
		IC.Options.addWrapper('enhancedView', IC.Language.$('view.options.wrapperTitle'));
		
		// Move the loading circle to the breadcrumb area.
		IC.Options.addCheckbox('moveLoadingCircle', 'enhancedView', 1, true, IC.Language.$('view.options.moveLoadingCircle'), {
			changeCallback: function(ib_newValue) {
				if(ib_newValue === true) {
					IC.myGM.addStyle(
						'#js_worldBread		{ margin-left: 16px !important; } \
						 #loadingPreview	{ transform: scale(0.5); -webkit-transform: scale(0.5); left: 35px !important; top: 141px !important; }',
						'enhancedView_moveLoadingCircle'
					);
					return;
				}
				
				IC.myGM.removeStyle('enhancedView_moveLoadingCircle');
			}
		});
		
		// Hide the flying bird swarm.
		IC.Options.addCheckbox('hideBirds', 'enhancedView', 1, true, IC.Language.$('view.options.hideBirds'), {
			changeCallback: function(ib_newValue) {
				if(ib_newValue === true) {
					IC.myGM.addStyle(
						'.bird_swarm	{ display: none; }',
						'enhancedView_hideBirds'
					);
					return;
				}
				
				IC.myGM.removeStyle('enhancedView_hideBirds');
			}
		});
		
		// Bind the town name and date to the top of the row in the town advisor.
		IC.Options.addCheckbox('noVerticalCenterInTownAdvisor', 'enhancedView', 1, true, IC.Language.$('view.options.noVerticalCenterInTownAdvisor'), {
			changeCallback: function(ib_newValue) {
				if(ib_newValue === true) {
					IC.myGM.addStyle(
						'#inboxCity td	{ vertical-align: top !important; }',
						'enhancedView_noVerticalCenterInTownAdvisor'
					);
					return;
				}
				
				IC.myGM.removeStyle('enhancedView_noVerticalCenterInTownAdvisor');
			}
		});
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: view functions created');
	
	// Functions for the island view.
	(function() {
		/**
		 * Storage for the colonizing city info functions.
		 * 
		 * @type	{object}
		 */
		var _go_colonizingCityInfo = new function() {
			/**
			 * Add the link to show the colonizing city information.
			 */
			var _lf_doShowColonizingCityInfo = function() {
				var la_colonizingCities = IC.myGM.$$('.level0');
			
				la_colonizingCities.forEach(function(le_colonizingCity) {
					var ls_locationId	= le_colonizingCity.id.replace(/\D/g, '');
					var ls_cityId		= IC.ika.getScreen().data.cities[ls_locationId].id;
					
					IC.myGM.$('#js_cityLocation' + ls_locationId + 'Link').href = '?view=cityDetails&destinationCityId=' + ls_cityId;
				});
			};
			
			/**
			 * Remove the link to show the colonizing city information.
			 */
			var _lf_doHideColonizingCityInfo = function() {
				var la_colonizingCityLinks = IC.myGM.$$('.level0 .link_img');
			
				la_colonizingCityLinks.forEach(function(le_colonizingCityLink) {
					le_colonizingCityLink.href = '';
				});
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showColonizingCityInfo
			 *   If the user selected the checkbox to show the colonizing city info.
			 */
			this.updateSettings = function(ib_showColonizingCityInfo) {
				if(IC.Ikariam.view !== 'island')
					return;
				
				if(ib_showColonizingCityInfo === true) {
					IC.RefreshHandler.add('*', 'showColonizingCityInfo', _lf_doShowColonizingCityInfo);
					_lf_doShowColonizingCityInfo();
					return;
				}
				
				IC.RefreshHandler.remove('*', 'showColonizingCityInfo');
				_lf_doHideColonizingCityInfo();
			};
		};
		
		// Retrieve information about colonies during colonization.
		IC.Options.addCheckbox('showColonizingCityInfo', 'diverseOptions', 1, true, IC.Language.$('island.options.showColonizingCityInfo'), { changeCallback: _go_colonizingCityInfo.updateSettings });
		// Add a divider line.
		IC.Options.addLine('diverseOptions', 1);
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: island functions created');
	
	// Functions for the finance popup.
	(function(){
		/**
		 * Some general functions for the finance view enhancements.
		 * 
		 * @type	{object}
		 */
		var _go_generalFunctions = new function() {
			/**
			 * Extract the current income in the finance popup.
			 * 
			 * @return	{int}
			 *   The current income.
			 */
			this.getIncome = function() {
				var le_incomeCell = IC.myGM.$$('.hidden')[IC.myGM.$$('.hidden').length - 1];
				
				while(le_incomeCell.firstChild.firstChild)
					le_incomeCell = le_incomeCell.firstChild;
				
				return IC.Ikariam.getInt(le_incomeCell.innerHTML);
			};
			
			/**
			 * Create a new table row.
			 * 
			 * @param	{element}	ie_parentTable
			 *   The table to add the row to.
			 * @param	{string[]}	ia_classes
			 *   The class(es) of the table row. Set to null if no class should be set.
			 * @param	{object[]}	ia_cells
			 *   The data for the table cells. Both properties of each cell are optional. If the cell object is empty, an empty cell is created.
			 *   Signature: <code>[{ className: ['class1', 'class2', ...], text: 'cellContent' }]</code>
			 * @param	{boolean}	ib_isTableHeadRow
			 *   If the cells should be table header cells (th).
			 * 
			 * @return	{element}
			 *   The created row.
			 */
			this.createTableRow = function(ie_parentTable, ia_classes, ia_cells, ib_isTableHeadRow) {
				var re_newRow	= IC.myGM.addElement('tr', ie_parentTable, { classes: ia_classes });
				var ls_cellType	= 'td';
				
				if(!!ib_isTableHeadRow)
					ls_cellType = 'th';
					
				for(var i = 0; i < ia_cells.length; i++) {
					var lo_options = {
						'classes':	ia_cells[i].className
					};
					
					if(!!ia_cells[i].text === true)
						lo_options['innerHTML'] = ia_cells[i].text;
						
					IC.myGM.addElement(ls_cellType, re_newRow, lo_options);
				}
				
				return re_newRow;
			};
		};
		
		/**
		 * Storage for the income on top functions.
		 * 
		 * @type	{object}
		 */
		var _go_incomeOnTop = new function() {
			/**
			 * Callback to show the income on top.
			 */
			var _lf_doShowIncomeOnTop = function() {
				var le_summaryTable	= IC.myGM.$('.table01');
				var li_income		= _go_generalFunctions.getIncome();
				
				var la_incomeCells = [
					{ className: ['sigma'], text: IC.Language.$('finance.income.perHour') },
					{ className: ['value', 'res'] },
					{ className: ['value', 'res'] },
					{ className: ['value', 'res'], text: IC.Ikariam.formatToIkaNumber(li_income) }
				];
				_go_generalFunctions.createTableRow(le_summaryTable, ['result', 'alt'], la_incomeCells);
				
				la_incomeCells[0].text = IC.Language.$('finance.income.perDay');
				la_incomeCells[3].text = IC.Ikariam.formatToIkaNumber(li_income * 24);
				_go_generalFunctions.createTableRow(le_summaryTable, ['result'], la_incomeCells);
				
				// Adjust the size of the Scrollbar.
				IC.ika.controller.adjustSizes();
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showIncomeOnTop
			 *   If the user selected the checkbox to show the income on top.
			 */
			this.updateSettings = function(ib_showIncomeOnTop) {
				if(ib_showIncomeOnTop === true) {
					IC.RefreshHandler.add('finances', 'showIncomeOnTop', _lf_doShowIncomeOnTop);
					return;
				}
				
				IC.RefreshHandler.remove('finances', 'showIncomeOnTop');
			};
		};
		
		/**
		 * Storage for the short upkeep reduction table functions.
		 * 
		 * @type	{object}
		 */
		var _go_shortUpkeepReductionTable = new function() {
			/**
			 * Extract the data from the old tables.
			 * 
			 * @param	{element[]}	ia_oldUpkeepReductionTables
			 *   The old upkeep tables to collapse.
			 *   
			 * @return	{object[]}
			 *   The data for the table rows.
			 *   Signature:
			 *   <code>[{
			 *     rows: [
			 *       { reason : 'reason', basicUpkeep: int, supplyUpkeep: int, result: int }
			 *     ]
			 *     priorToReduction: int
			 *     income: int
			 *   }]</code>
			 */
			var _lf_getData = function(ia_oldUpkeepReductionTables) {
				var ro_data = {
					rows:				[],
					priorToReduction:	IC.Ikariam.getInt(IC.myGM.$('td.hidden', ia_oldUpkeepReductionTables[2]).innerHTML),
					income:				_go_generalFunctions.getIncome()
				};
				
				// Get the data for the troops and ships redution rows.
				for(var i = 0; i < 3; i++) {
					var le_basicUpkeepCell	= IC.myGM.$$('.alt.bottomLine td.hidden, .result td.hidden', ia_oldUpkeepReductionTables[0])[i];
					var le_supplyUpkeepCell	= IC.myGM.$$('.alt.bottomLine td.hidden, .result td.hidden', ia_oldUpkeepReductionTables[1])[i];
					var li_basicUpkeep		= IC.Ikariam.getInt(le_basicUpkeepCell.innerHTML);
					var li_supplyUpkeep		= IC.Ikariam.getInt(le_supplyUpkeepCell.innerHTML);
					
					var lo_row = {
						reason:			IC.Language.$('finance.upkeep.reason.' + i),
						basicUpkeep:	li_basicUpkeep,
						supplyUpkeep:	li_supplyUpkeep,
						result:			li_basicUpkeep + li_supplyUpkeep
					};
					
					ro_data.rows.push(lo_row);
				}
				
				return ro_data;
			};
			
			/**
			 * Prepare the table rows for easy adding to the new table.
			 * 
			 * @param	{element[]}	ia_oldUpkeepReductionTables
			 *   The old upkeep tables to collapse.
			 *   
			 * @return	{object[]}
			 *   The data for the table rows.
			 *   Signature:
			 *   <code>[{
			 *     classes:		'class' || ['class1', 'class2', ...]
			 *     isHeadRow:	true || false
			 *     cells:	[
			 *       { className: 'class' || ['class1', 'class2', ...], text: 'cellContent' (optional) }
			 *     ]
			 *   }]</code>
			 */
			var _lf_prepareTableRows = function(ia_oldUpkeepReductionTables) {
				var lo_data = _lf_getData(ia_oldUpkeepReductionTables);
				var ra_tableRows = [];
				
				// Header
				ra_tableRows.push({
					classes:	[],
					isHeadRow:	true,
					cells:		[
						 { className: ['city'] },
						 { className: ['value', 'res'], text: IC.Language.$('finance.upkeep.basic') },
						 { className: ['value', 'res'], text: IC.Language.$('finance.upkeep.supply') },
						 { className: ['value', 'res'], text: IC.Language.$('finance.upkeep.result') }
					]
				});
				
				// Income without reduction
				ra_tableRows.push({
					classes:	['alt', 'bottomLine'],
					isHeadRow:	false,
					cells:		[
						{ className: ['city'], text: IC.Language.$('finance.income.start') },
						{ className: ['value', 'res'] },
						{ className: ['value', 'res'] },
						{ className: ['value', 'res'], text: IC.Ikariam.formatToIkaNumber(lo_data.priorToReduction) }
					]
				});
				
				// Income reduction
				lo_data.rows.forEach(function(io_row, ii_index) {
					var trClass = [];
					if(ii_index % 2 === 1)
						trClass = ['alt', 'bottomLine'];
					
					ra_tableRows.push({
						classes:	trClass,
						isHeadRow:	false,
						cells:		[
							{ className: ['city'],			text: io_row.reason },
							{ className: ['value', 'res'],	text: IC.Ikariam.formatToIkaNumber(-io_row.basicUpkeep) },
							{ className: ['value', 'res'],	text: IC.Ikariam.formatToIkaNumber(-io_row.supplyUpkeep) },
							{ className: ['hidden'],			text: IC.Ikariam.formatToIkaNumber(-io_row.result) }
						]
					});
				});
				
				// Result
				ra_tableRows.push({
					classes:	['result'],
					isHeadRow:	false,
					cells:		[
						{ className: ['sigma'], text: '<img alt="Summe" src="skin/layout/sigma.png">' },
						{ className: ['value', 'res'] },
						{ className: ['value', 'res'] },
						{ className: ['hidden'], text: IC.Ikariam.formatToIkaNumber(lo_data.income) }
					]
				});
				
				return ra_tableRows;
			};
			
			/**
			 * Create the new upkeep reduction table.
			 * 
			 * @param	{element[]}	ia_oldUpkeepReductionTables
			 *   The old upkeep tables.
			 */
			var _lf_createNewTable = function(ia_oldUpkeepReductionTables) {
				var le_shortTable	= IC.myGM.addElement('table', ia_oldUpkeepReductionTables[0].parentNode, { 
					'id':		'balance',
					'classes':	['table01', 'border', 'left']
				}, null, ia_oldUpkeepReductionTables[0]);
				
				var la_tableRows = _lf_prepareTableRows(ia_oldUpkeepReductionTables);
				
				la_tableRows.forEach(function(io_rowData) {
					_go_generalFunctions.createTableRow(le_shortTable, io_rowData.classes, io_rowData.cells, io_rowData.isHeadRow);
				});
				
				IC.myGM.addElement('hr', ia_oldUpkeepReductionTables[0].parentNode, {}, null, ia_oldUpkeepReductionTables[0]);
			};
			
			/**
			 * Toggle the status of an old upkeep table (collapsed / non-collapsed).
			 */
			var _lf_toggleUpkeepReductionTable = function() {
				IC.myGM.toggleShowHideButton(this);
				
				var la_rows = IC.myGM.$$('tr:not(:first-child)', this.parentNode.parentNode.parentNode);
				
				la_rows.forEach(function(ie_row) {
					ie_row.classList.toggle('invisible');
				});
				
				// Adjust the size of the Scrollbar.
				IC.ika.controller.adjustSizes();
			};
			
			/**
			 * Minimize the old upkeep tables and make them collapsable.
			 * 
			 * @param	{element[]}	ia_oldUpkeepReductionTables
			 *   The old upkeep tables to collapse.
			 */
			var _lf_minimizeOldTables = function(ia_oldUpkeepReductionTables) {
				for(var i = 0; i < ia_oldUpkeepReductionTables.length; i++) {
					var la_rows = IC.myGM.$$('tr', ia_oldUpkeepReductionTables[i]);
					
					la_rows.forEach(function(ie_row, ii_index) {
						if(ii_index !== 0)
							ie_row.classList.add('invisible');
					});
					
					var le_buttonParent	= IC.myGM.$('th', la_rows[0]);
					IC.myGM.addElement('div', le_buttonParent, {
						'class':	'maximizeImg',
						'style':	[['cssFloat', 'left']],
						'title':	IC.Language.$('general.expand'),
						'click':	_lf_toggleUpkeepReductionTable
					}, null, le_buttonParent.firstChild);
				}
			};
			
			/**
			 * Callback to show the short upkeep reduction table.
			 */
			var _lf_doShowShortTable = function() {
				var la_oldUpkeepReductionTables = IC.myGM.$$('.upkeepReductionTable');
				
				_lf_createNewTable(la_oldUpkeepReductionTables);
				_lf_minimizeOldTables(la_oldUpkeepReductionTables);
				
				// Adjust the size of the Scrollbar.
				IC.ika.controller.adjustSizes();
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showShortTable
			 *   If the user selected the checkbox to show the short upkeep reduction table.
			 */
			this.updateSettings = function(ib_showShortTable) {
				if(ib_showShortTable === true) {
					IC.RefreshHandler.add('finances', 'shortUpkeepReductionTable', function() { _lf_doShowShortTable(); });
					return;
				}
				
				IC.RefreshHandler.remove('finances', 'shortUpkeepReductionTable');
			};
		};
		
		// Show the income also on top in finance popup.
		IC.Options.addCheckbox('showIncomeOnTop', 'diverseOptions', 1, true, IC.Language.$('finance.options.showIncomeOnTop'), { changeCallback: _go_incomeOnTop.updateSettings });
		// Short overview upkeep reduction table in finance popup.
		IC.Options.addCheckbox('shortUpkeepReductionTable', 'diverseOptions', 1, true, IC.Language.$('finance.options.shortUpkeepReductionTable'), { changeCallback: _go_shortUpkeepReductionTable.updateSettings });
		// Add a divider line.
		IC.Options.addLine('diverseOptions', 1);
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: finance functions created');
	
	// Functions for missing resources.
	(function() {
		/**
		 * Storage for the missing ressources functions.
		 * 
		 * @type	{object}
		 */
		var _go_showMissingResources = new function() {
			/**
			 * Update the information in the missing resources wrappers.
			 * 
			 * @param	{boolean}	ib_isUpdateView
			 *   If the update is in the update view.
			 */
			var _lf_updateInformation = function(ib_isUpdateView) {
				var la_currentResources = [
					IC.ika.getModel().currentResources.resource,	// Wood.
					IC.ika.getModel().currentResources[1],			// Wine.
					IC.ika.getModel().currentResources[2],			// Marble.
					IC.ika.getModel().currentResources[3],			// Crystal.
					IC.ika.getModel().currentResources[4]			// Sulfur.
				];
				
				var lb_showPositiveNumbers	= IC.Options.getOption('missingResources', 'showPositive') === true && ib_isUpdateView === true;
				var lb_showMissingColoured	= IC.Options.getOption('missingResources', 'showColoured');
				
				var ls_resourcesPattern	= '%resources%';
				if(ib_isUpdateView !== true)
					ls_resourcesPattern = ' (' + ls_resourcesPattern + ')';
				
				for(var i = 0; i < IC.Ikariam.resourceNames.length; i++) {
					var la_missingWrappers = IC.myGM.$$('.' + IC.myGM.prefix + 'missingResources' + IC.Ikariam.resourceNames[i]);
					
					if(!!la_missingWrappers) {
						for(var k = 0; k < la_missingWrappers.length; k++) {
							
							var le_neededWrapper	= la_missingWrappers[k].previousSibling;
							var li_missing			= la_currentResources[i] - IC.Ikariam.getInt(le_neededWrapper.nodeValue);
							
							if(li_missing < 0 || lb_showPositiveNumbers === true) {
								var ls_formattedMissingValue	= IC.Ikariam.formatToIkaNumber(li_missing, lb_showMissingColoured, true);
								la_missingWrappers[k].innerHTML	= ls_resourcesPattern.replace(/\%resources\%/, ls_formattedMissingValue);
							} else {
								la_missingWrappers[k].innerHTML = '';
							}
						}
					}
				}
			};
			
			/**
			 * Add the missing resources wrappers.
			 * 
			 * @param	{boolean}	ib_isUpdateView
			 *   If the wrappers are added to the update view.
			 */
			var _lf_addMissingWrappers = function(ib_isUpdateView) {
				var ls_wrapperSelector = '#buildingGround .resources';
				if(ib_isUpdateView === true)
					ls_wrapperSelector = '#sidebar .resources';
				
				var la_wrappers = IC.myGM.$$(ls_wrapperSelector);
				
				for(var i = 0; i < la_wrappers.length; i++) {
					for(var k = 0; k < IC.Ikariam.resourceNames.length; k++) {
						var le_resourceNode = IC.myGM.$('.' + IC.Ikariam.resourceNames[k], la_wrappers[i]);
						
						if(!!le_resourceNode) {
							IC.myGM.addElement('span', le_resourceNode, { 'classes': ['missingResources', 'missingResources' + IC.Ikariam.resourceNames[k]] }, true);
						}
					}
				}
				
				_lf_updateInformation(ib_isUpdateView);
			};
			
			/**
			 * Update the missing resources in building ground popup.
			 */
			var _lf_doUpdateInBuildingView = function() {
				if(!IC.myGM.$('#buildingGround_c')) {
					IC.Observer.remove('missingResourcesBuilding');
					return;
				}

				_lf_updateInformation(false);
			};
			
			/**
			 * Update the missing resources in update view.
			 */
			var _lf_doUpdateInUpdateView = function() {
				if(!IC.myGM.$('#buildingUpgrade')) {
					IC.Observer.remove('missingResourcesUpdate');
					return;
				}

				_lf_updateInformation(true);
			};
			
			/**
			 * Show the missing resources in building ground popup.
			 */
			var _lf_doShowInBuildingGround = function() {
				_lf_addMissingWrappers(false);
				
				IC.Observer.add('missingResourcesBuilding', IC.myGM.$('#cityResources'), { childList: true, subtree: true }, _lf_doUpdateInBuildingView, _lf_doUpdateInBuildingView);
			};
			
			/**
			 * Show the missing resources in update view.
			 */
			var _lf_doShowInUpdateView = function() {
				if(!IC.myGM.$('#buildingUpgrade') || IC.myGM.$$('.' + IC.myGM.prefix + 'missingResources').length > 0)
					return;
				
				_lf_addMissingWrappers(true);
				
				IC.Observer.add('missingResourcesUpgrade', IC.myGM.$('#cityResources'), { childList: true, subtree: true }, _lf_doUpdateInUpdateView, _lf_doUpdateInUpdateView);
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showMissingResources
			 *   If the user selected the checkbox to show the missing resources info.
			 */
			this.updateSettings = function(ib_showMissingResources) {
				if(ib_showMissingResources === true) {
					IC.RefreshHandler.add('buildingGround', 'showMissingResources', _lf_doShowInBuildingGround);
					IC.RefreshHandler.add('%', 'showMissingResources', _lf_doShowInUpdateView);
					IC.myGM.addStyle(
						'#sidebar #buildingUpgrade ul.resources li			{ width: 120px; } \
						 #sidebar #buildingUpgrade ul.resources li.time		{ width: 60px !important; } \
						 #sidebar .' + IC.myGM.prefix + 'missingResources	{ float: right; }',
						'showMissingResources'
					);
					return;
				}
				
				IC.RefreshHandler.remove('buildingGround', 'showMissingResources');
				IC.RefreshHandler.remove('%', 'showMissingResources');
				IC.myGM.removeStyle('showMissingResources');
			};
		};
		
		IC.Options.addWrapper('missingResources', IC.Language.$('missingResources.options.wrapperTitle'));
		
		// Show missing resources in upgrade / building view.
		IC.Options.addCheckbox('show', 'missingResources', 1, true, IC.Language.$('missingResources.options.show'), { changeCallback: _go_showMissingResources.updateSettings });
		
		// Disable coloring of the missing resources.
		IC.Options.addCheckbox('showPositive', 'missingResources', 2, true, IC.Language.$('missingResources.options.showPositive'), {});
		// Show only missing ressources, not also remaining.
		IC.Options.addCheckbox('showColoured', 'missingResources', 2, true, IC.Language.$('missingResources.options.showColoured'), {});
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: missing resource functions created');
	
	// Functions for tooltips.
	(function() {
		/**
		 * Storage for the autoshow tootips functions.
		 * 
		 * @type	{object}
		 */
		var _go_autoshowTooltips = new function() {
			/**
			 * General function for autowhow.
			 * 
			 * @param	{string}	onclickWrapperClass
			 *   The class of the wrapper which has the onclik attribute set.
			 */
			var _lf_autoshowTooltipsGeneral = function(onclickWrapperClass) {
				IC.myGM.$$('.' + onclickWrapperClass).forEach(function(onclickWrapper) {
					var onclickFunction		= onclickWrapper.onclick;
					onclickWrapper.onclick	= 'return false;';
					
					var newHandler = IC.myGM.$('.magnify_icon', onclickWrapper);
					if(!newHandler)
						newHandler = onclickWrapper;
					
					newHandler.addEventListener('mouseover', function(e) { IC.ika.controller.captureMousePosition(e); onclickFunction(e); }, true);
				});
				
				IC.myGM.$('.templateView .mainContent').addEventListener('click', function() { IC.win.$(document).trigger("closeExclusiveInfo"); }, true);
			};
			
			/**
			 * Autmatically show tooltips in alliance member lists.
			 */
			var _lf_doAutoshowTooltipsAlliance = function() {
				_lf_autoshowTooltipsGeneral('cityInfo');
			};
			
			/**
			 * Autmatically show tooltips in military advisor.
			 */
			var _lf_doAutoshowTooltipsMilitaryAdvisor = function() {
				if(IC.Options.getOption('moduleOptions', 'showDirectMilitaryTooltips') === true)
					return;
				
				_lf_autoshowTooltipsGeneral('spyMilitary');
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	autoshowTooltips
			 *   If the user selected the checkbox to autoshow tooltips.
			 */
			this.updateSettings = function(autoshowTooltips) {
				var alliancePopupIds = ['diplomacyAllyMemberlist', 'embassy'];
				
				if(autoshowTooltips === true) {
					IC.RefreshHandler.add('militaryAdvisor', 'autoshowTooltips', _lf_doAutoshowTooltipsMilitaryAdvisor);
					IC.RefreshHandler.add(alliancePopupIds, 'autoshowTooltips', _lf_doAutoshowTooltipsAlliance);
					return;
				}
				
				IC.RefreshHandler.remove('militaryAdvisor', 'autoshowTooltips');
				IC.RefreshHandler.remove(alliancePopupIds, 'autoshowTooltips');
			};
		};
		
		/**
		 * Storage for the direct military tooltip functions.
		 * 
		 * @type	{object}
		 */
		var _go_directMilitaryTooltips = new function() {
			/**
			 * Storage for the class ids and their corresponding translations. Each class is a property of the object with the translation stored in it.
			 * 
			 * @type	{object}
			 */
			var _lo_idTranslation = {};
			
			/**
			 * Fill the id translation object.
			 */
			var _lf_fillIdTranslation = function() {
				if(_lo_idTranslation.length === 0)
					return;
				
				var la_troops		= ['swordsman', 'phalanx', 'archer', 'marksman', 'mortar', 'slinger', 'catapult', 'ram', 'steamgiant', 'bombardier', 'cook', 'medic', 'gyrocopter', 'spearman', 'spartan'];
				var la_ships		= ['balliasta', 'catapult', 'flamethrower', 'mortar', 'ram', 'steamboat', 'rocketship', 'submarine', 'paddlespeedship', 'balloncarrier', 'tender', 'transport', 'premium_transport'];
				var la_resources	= IC.Ikariam.resourceNames;
				la_resources.push('gold');
				
				la_troops.forEach(function(is_troopName) {
					_lo_idTranslation[is_troopName] = IC.Language.$('diverse.name.unit.' + is_troopName);
				});
				
				la_ships.forEach(function(is_shipName) {
					_lo_idTranslation['ship_' + is_shipName] = IC.Language.$('diverse.name.ship.' + is_shipName.replace('premium_', ''));
				});
				
				la_resources.forEach(function(is_resourceName) {
					_lo_idTranslation[is_resourceName] = IC.Language.$('diverse.name.resource.' + is_resourceName);
				});
			};
			
			/**
			 * Hide the number of ships for the own peaceful missions (trade / transport).
			 */
			var _lf_hideShipNumberOwnPeacefulMissions = function() {
				var la_ownEventTableRows = IC.myGM.$$('#js_MilitaryMovementsFleetMovementsTable .military_event_table tr.own');
				
				la_ownEventTableRows.forEach(function(ie_tableRow) {
					var le_missionDiv		= IC.myGM.$('td:nth-child(1) div.mission_icon', ie_tableRow);
					var lb_peacefulMission	= le_missionDiv.classList.contains('transport') || le_missionDiv.classList.contains('trade');
					
					if(lb_peacefulMission === true) {
						IC.myGM.$('td:nth-child(4) div', ie_tableRow).classList.add('invisible');
					}
				});
			};
			
			/**
			 * Show the military tooltips directly.
			 */
			var _lf_doShowDirectMilitaryTooltips = function() {
				_lf_fillIdTranslation();
				
				_lf_hideShipNumberOwnPeacefulMissions();
				
				IC.myGM.$$('.spyMilitary').forEach(function(ie_movementWrapper) {
					ie_movementWrapper.onclick = 'return false;';
				});
				
				var le_movementsTable = IC.myGM.$('#js_MilitaryMovementsFleetMovementsTable');
				
				IC.myGM.forEach(_lo_idTranslation, function(is_classId, is_translation) {
					IC.myGM.$$('.icon40.' + is_classId, le_movementsTable).forEach(function(ie_detailIcon) {
						ie_detailIcon.title = is_translation;
					});
				});
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showDirectMilitaryTooltips
			 *   If the user selected the checkbox to show the military tooltips directly.
			 */
			this.updateSettings = function(ib_showDirectMilitaryTooltips) {
				if(ib_showDirectMilitaryTooltips === true) {
					IC.RefreshHandler.add('militaryAdvisor', 'showDirectMilitaryTooltips', _lf_doShowDirectMilitaryTooltips);
					IC.myGM.addStyle(
						'#js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon				{ background-image: none; cursor: default; width: 240px; } \
						 #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip		{ display: inline; position: relative; padding: 0px; border: none; } \
						 #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip h5	{ display: none; } \
						 #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40						{ background-size: 25px 25px; background-color: transparent; padding: 26px 3px 0px 3px; width: 30px; } \
						 #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40.resource_icon		{ background-size: 20px 16px; }',
						'showDirectMilitaryTooltips'
					);
					return;
				}
				
				IC.RefreshHandler.remove('militaryAdvisor', 'showDirectMilitaryTooltips');
				IC.myGM.removeStyle('showDirectMilitaryTooltips');
			};
		};
		
		// Show alliance / military tooltips directly.
		IC.Options.addCheckbox('autoshowTooltips', 'diverseOptions', 1, true, IC.Language.$('tooltips.options.autoshow'), { changeCallback: _go_autoshowTooltips.updateSettings });
		// Show military tooltips directly.
		IC.Options.addCheckbox('showDirectMilitaryTooltips', 'diverseOptions', 1, false, IC.Language.$('tooltips.options.showDirectInMilitaryAdvisor'), { changeCallback: _go_directMilitaryTooltips.updateSettings });
		// Add a divider line.
		IC.Options.addLine('diverseOptions', 1);
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: tooltip functions created');
	
	// Zoom function.
	(function() {
		/**
		 * Storage for the zoom functions.
		 * 
		 * @type	{object}
		 */
		var _go_zoomView = new function() {
			/**
			 * Storage for the minimum zoom factor.
			 * 
			 * @type	{int}
			 */
			var _li_minZoom	= 55;
			
			/**
			 * Storage for the maximum zoom factor.
			 * 
			 * @type	{int}
			 */
			var _li_maxZoom	= 150;
			
			/**
			 * Storage for the zoom step size.
			 * 
			 * @type	{int}
			 */
			var _li_zoomStep	= 5;
			
			/**
			 * Storage for the actual mousewheel callback.
			 * 
			 * @type	{function}
			 */
			var _lf_mousewheelCallbackStorage = IC.ika.getController().mouseScrollHandle;
			
			/**
			 * Getter for the minimum zoom factor.
			 * 
			 * @return	{int}
			 *   The minimum zoom factor.
			 */
			this.__defineGetter__('minZoom', function() { return _li_minZoom; });
			
			/**
			 * Getter for the maximum zoom factor.
			 * 
			 * @return	{int}
			 *   The maximum zoom factor.
			 */
			this.__defineGetter__('maxZoom', function() { return _li_maxZoom; });
			
			/**
			 * Getter for the zoom step size.
			 * 
			 * @return	{int}
			 *   The zoom step size.
			 */
			this.__defineGetter__('zoomStep', function() { return _li_zoomStep; });
			
			/**
			 * Initialize the zoom bounds (min and max zoom).
			 */
			var _lf_initBounds = function() {
				var li_minZoom = Math.round(IC.ika.worldview_scale_min * 100);
				if(li_minZoom % 5 != 0)
					li_minZoom = li_minZoom + (5 - (li_minZoom % 5));

				_li_minZoom = li_minZoom;
				
				IC.ika.worldview_scale_min = _li_minZoom / 100;
				IC.ika.worldview_scale_max = _li_maxZoom / 100;
			};
			
			/**
			 * Zoom the world view, as there is no function provided by Ikariam.
			 * 
			 * @param	{decimal}	in_zoomFactor
			 *   The zoom factor as decimal number (1 =^= 100%)
			 */
			var _lf_zoomWorldView = function(in_zoomFactor) {
				var li_translateXY		= (100 - 100 / in_zoomFactor) / 2;
				var ls_heightWidth		= (100 / in_zoomFactor) + '% !important';
				var ls_transformString	= 'scale(' + in_zoomFactor + ') translate(' + li_translateXY + '%, ' + li_translateXY + '%)';
				
				IC.myGM.addStyle(
					'#scrollcover	{ transform: ' + ls_transformString + '; -webkit-transform: ' + ls_transformString + '; height: ' + ls_heightWidth + '; width: ' + ls_heightWidth + '; }',
					'zoomWorld', true
				);
		
				var le_map = IC.myGM.$('#map1');
				
				le_map.style.top	= '0px';
				le_map.style.left	= '0px';
			};
			
			/**
			 * Rescale the badges and symbols.
			 * 
			 * @param	{decimal}	in_zoomFactorNumber
			 *   The zoom factor as decimal number (1 =^= 100%)
			 */
			var _lf_scaleChildren = function(in_zoomFactorNumber) {
				var ls_transformString	= 'transform: scale(' + 1 / in_zoomFactorNumber + ');  -webkit-transform: scale(' + 1 / in_zoomFactorNumber + ');';
				var ls_style			= '';
				
				if(IC.Ikariam.view == 'world') {
					var ls_ownerState = '';
					
					if(in_zoomFactorNumber < 1)
						ls_ownerState = ', .ownerState';
					
					ls_style =	'.islandTile .wonder, .islandTile .tradegood, .islandTile .cities, .islandTile .piracyInRange' + ls_ownerState + '	{ ' + ls_transformString + ' } \
						 		 .islandTile .cities																	{ bottom: 10px !important; }';
				}
				
				if(IC.Ikariam.view == 'island') {
					var ls_movePiracy = 'transform: translate(0px, -' + (1 - in_zoomFactorNumber) * 20 + 'px) scale(' + 1 / in_zoomFactorNumber + ');';
					ls_style =	'.cityLocation .scroll_img, .cityLocationScroll .scroll_img	{ ' + ls_transformString + ' } \
								 .cityLocation .piracyRaid									{ ' + ls_movePiracy + ' }';
				}
				
				if(IC.Ikariam.view == 'town')
					ls_style = '.timetofinish	{ ' + ls_transformString + ' }';
				
				IC.myGM.addStyle(ls_style, 'scaleChildren', true);
			};
			
			/**
			 * Update the zoom in and zoom out button and the percentage in the middle.
			 * 
			 * @param	{int}	ii_zoomFactor
			 *   The zoom factor as percentage.
			 */
			var _lf_updateZoomButtons = function(ii_zoomFactor) {
				var le_zoomFactorDiv	= IC.myGM.$('#' + IC.myGM.prefix + 'zoomFactor');
				var le_zoomIn			= IC.myGM.$('#' + IC.myGM.prefix + 'zoomIn');
				var le_zoomOut			= IC.myGM.$('#' + IC.myGM.prefix + 'zoomOut');
		
				if(!!le_zoomFactorDiv) {
					le_zoomFactorDiv.innerHTML = ii_zoomFactor + '%';
				}
				
				if(!!le_zoomIn) {
					le_zoomIn.style.visibility = '';
					
					if(ii_zoomFactor >= _li_maxZoom)
						le_zoomIn.style.visibility = 'hidden';
				}
				
				if(!!le_zoomOut) {
					le_zoomOut.style.visibility = '';
					
					if(ii_zoomFactor <= _li_minZoom)
						le_zoomOut.style.visibility = 'hidden';
				}
			};
			
			/**
			 * Calculate the zoom factor as decimal number and execute the zoom function. If it is requestet, call also the rescale function for child elements.
			 * 
			 * @param	{int}	ii_zoomFactor
			 *   The zoom factor as percentage.
			 */
			var _lf_zoomView = function(ii_zoomFactor) {
				var ln_zoomFactorNumber = ii_zoomFactor / 100.0;
	
				var li_scale = 0;
				
				if(IC.Ikariam.view == 'island')
					li_scale = IC.ika.worldview_scale_island;
					
				if(IC.Ikariam.view == 'town')
					li_scale = IC.ika.worldview_scale_city;
				
				if(IC.Ikariam.view == 'world') {
					_lf_zoomWorldView(ln_zoomFactorNumber);
				} else {
					var li_stepNumber = Math.round((ln_zoomFactorNumber - li_scale) / .05);
		
					var le_worldview	= IC.myGM.$('#worldview');
					var li_posX			= le_worldview.offsetLeft + le_worldview.offsetWidth / 2;
					var li_posY			= le_worldview.offsetTop + le_worldview.offsetHeight / 2;
					
					IC.ika.controller.scaleWorldMap(li_stepNumber, li_posX, li_posY);
				}
				
				if(IC.Options.getOption('zoom', IC.Ikariam.view + 'ScaleChildren') === true)
					_lf_scaleChildren(ln_zoomFactorNumber);
			};
			
			/**
			 * Ensure that the requested zoom factor is in the bounds and update the view.
			 * 
			 * @param	{int}	ii_zoomFactor
			 *   The zoom factor as percentage.
			 */
			var _lf_zoom = function(ii_zoomFactor) {
				if(ii_zoomFactor > _li_maxZoom)
					ii_zoomFactor = _li_maxZoom;
				
				if(ii_zoomFactor < _li_minZoom)
					ii_zoomFactor = _li_minZoom;
				
				IC.Options.setOption('zoom', IC.Ikariam.view + 'Factor', ii_zoomFactor);
				
				_lf_updateZoomButtons(ii_zoomFactor);
				_lf_zoomView(ii_zoomFactor);
			};
			
			/**
			 * Zoom one step in.
			 */
			var _lf_zoomIn = function() {
				var li_factor = IC.Ikariam.getInt(IC.Options.getOption('zoom', IC.Ikariam.view + 'Factor', 100)) + _li_zoomStep;
	
				_lf_zoom(li_factor);
			};
			
			/**
			 * Zoom one step out.
			 */
			var _lf_zoomOut = function() {
				var li_factor = IC.Options.getOption('zoom', IC.Ikariam.view + 'Factor', 100) - _li_zoomStep;
	
				_lf_zoom(li_factor);
			};
			
			/**
			 * Add the zoom buttons.
			 */
			var _lf_addZoomButtons = function() {
				var le_zoomWrapper	= IC.myGM.addElement('div', ge_toolbar, { 'id': 'zoomWrapper' });
				IC.myGM.addElement('div', le_zoomWrapper, {
					'id':		'zoomIn',
					'class':	'maximizeImg',
					'title':	IC.Language.$('zoom.zoomIn'),
					'click':	_lf_zoomIn
				});
				IC.myGM.addElement('div', le_zoomWrapper, {
					'id':		'zoomFactor',
					'title':	IC.Language.$('zoom.factor')
				});
				IC.myGM.addElement('div', le_zoomWrapper, {
					'id':		'zoomOut',
					'class':	'minimizeImg',
					'title':	IC.Language.$('zoom.zoomOut'),
					'click':	_lf_zoomOut
				});
				
				IC.myGM.addStyle(
					'#' + IC.myGM.prefix + 'zoomWrapper			{ width: 72px; margin: 0px -5px !important; transform: scale(0.75); scale(0.75); -webkit-transform: scale(0.75); } \
					 #' + IC.myGM.prefix + 'zoomWrapper > div	{ display: inline-block; position: relative; } \
					 #' + IC.myGM.prefix + 'zoomFactor			{ top: -4px; width: 35px; text-align: center; }',
					'zoomButtons'
				);
			};
			
			/**
			 * Set the zoom to 100% if in world view or if it was bigger than 100% and remove the zoom buttons and all styles for the zoom.
			 */
			var _lf_resetZoom = function() {
				if((IC.Ikariam.view == 'island' && IC.ika.worldview_scale_island > 1) || (IC.Ikariam.view == 'town' && IC.ika.worldview_scale_city > 1) || IC.Ikariam.view == 'world')
					_lf_zoomView(100);
					
				IC.ika.worldview_scale_max = 1;
				
				IC.myGM.removeElement(IC.myGM.$('#' + IC.myGM.prefix + 'zoomWrapper'));
				
				IC.myGM.removeStyle('zoomButtons');
				IC.myGM.removeStyle('zoomWorld');
				IC.myGM.removeStyle('scaleChildren');
			};
			
			/**
			 * Check, if the key are pressed which are required to zoom with the mouse.
			 * 
			 * @param	{object}	io_event
			 *   The "event object" with information about the mouse scroll and pressed keys.
			 * 
			 * @return	{boolean}
			 *   If the correct keys were pressed.
			 */
			var _lf_keysOK = function(io_event) {
				var lb_ctrlOK = !!io_event.ctrlKey;
				if(IC.Options.getOption('zoom', 'ctrlAsAccessKey') === false)
					lb_ctrlOK = true;
				
				var lb_altOK = !!io_event.altKey;
				if(IC.Options.getOption('zoom', 'altAsAccessKey') === false)
					lb_altOK = true;
				
				var lb_shiftOK = !!io_event.shiftKey;
				if(IC.Options.getOption('zoom', 'shiftAsAccessKey') === false)
					lb_shiftOK = true;
				
				return lb_ctrlOK && lb_altOK && lb_shiftOK;
			};
			
			/**
			 * Calculate the delta the mousewheel was turned.
			 * 
			 * @param	{object}	io_event
			 *   The "event object" with information about the mouse scroll and pressed keys.
			 * 
			 * @return	{int}
			 *   The delta the mousewheel was moved.
			 */
			var _lf_calculateDelta = function(io_event) {
				var ri_stepNumber = 0;

				// Get the number of steps to scroll.
				if(io_event.wheelDelta)
					ri_stepNumber = io_event.wheelDelta / 120;

				if (io_event.detail)
					ri_stepNumber = -io_event.detail / 3;

				if (io_event.wheelDeltaY !== undefined)
					ri_stepNumber = io_event.wheelDeltaY / 120;
				
				// If the number is between -1 and 0, set it to -1.
				if(ri_stepNumber < 0)
					ri_stepNumber = ri_stepNumber > -1 ? -1 : Math.round(ri_stepNumber);

				// If the number is between 0 and 1, set it to 1.
				else
					ri_stepNumber = ri_stepNumber < 1 ? 1 : Math.round(ri_stepNumber);
					
				return ri_stepNumber;
			};
			
			/**
			 * Handler for mousescroll to zoom.
			 * 
			 * @param	{object}	io_event
			 *   The "event object" with information about the mouse scroll and pressed keys.
			 * 
			 * @return	false
			 *   If the prevent default method is not available to prevent the default action.
			 */
			var _lf_mouseScroll = function(io_event) {
				if(_lf_keysOK(io_event) === true) {
					// If the scrolling is horizontally return.
					if(io_event.axis !== undefined && io_event.axis === io_event.HORIZONTAL_AXIS)
						return;
					
					var li_factor = IC.Ikariam.getInt(IC.Options.getOption('zoom', IC.Ikariam.view + 'Factor')) + _li_zoomStep * _lf_calculateDelta(io_event);
					
					_lf_zoom(li_factor);
					
					// Prevent the default event.
					if(io_event.preventDefault)
						io_event.preventDefault();
					else
						return false;
				}
			};
		
			/**
			 * Change the mousewheel listener to a new callback.
			 * 
			 * @param	{function}	if_newCallback
			 *   The new callback for the mousewheel listener.
			 */
			var _lf_changeMouseWheelListener = function(if_newCallback) {
				if(_lf_mousewheelCallbackStorage == if_newCallback)
					return;
				
				var ls_scrollDivId = '#worldmap';
				
				if(IC.Ikariam.view == 'world')
					ls_scrollDivId	= '#map1';
				
				if(_lf_mousewheelCallbackStorage == IC.ika.getController().mouseScrollHandle) {
					// Remove the ikariam mouse wheel listener and add the own. (with the use of Ikariam-jQuery)
					IC.win.$(ls_scrollDivId).unbind('mousewheel');
					
					var scrollDiv = IC.myGM.$(ls_scrollDivId);
					scrollDiv.addEventListener('DOMMouseScroll', if_newCallback, false);
					scrollDiv.addEventListener('mousewheel', if_newCallback, false);
				} else {
					var scrollDiv = IC.myGM.$(ls_scrollDivId);
					scrollDiv.removeEventListener('DOMMouseScroll', _lf_mousewheelCallbackStorage, false);
					scrollDiv.removeEventListener('mousewheel', _lf_mousewheelCallbackStorage, false);
					
					IC.win.$(ls_scrollDivId).on('mousewheel', if_newCallback);
				}
				
				_lf_mousewheelCallbackStorage = if_newCallback;
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_zoomView
			 *   If the user selected the checkbox to zoom.
			 */
			this.updateSettings = function(ib_zoomView) {
				_lf_initBounds();
				_go_zoomOptions.renewFactorSelects();
				
				if(ib_zoomView === true) {
					_lf_addZoomButtons();
					_lf_changeMouseWheelListener(_lf_mouseScroll);
					_lf_zoom(IC.Options.getOption('zoom', IC.Ikariam.view + 'Factor'));
					return;
				}
				
				_lf_resetZoom();
				_lf_changeMouseWheelListener(IC.ika.getController().mouseScrollHandle);
			};
		};
		
		/**
		 * Storage for the option creation functions.
		 * 
		 * @type	{object}
		 */
		var _go_zoomOptions = new function() {
			/**
			 * If the zoom wrapper is already created.
			 */
			var _lb_zoomWrapperCreated	= false;
			
			/**
			 * Add the select fields for the zoom factors.
			 */
			var _lf_addFactorSelects = function(ib_replace) {
				var la_options = [];
				
				for(var i = _go_zoomView.minZoom; i <= _go_zoomView.maxZoom; i = i + _go_zoomView.zoomStep) {
					la_options.push({ value: i, label: i + '%' });
				}
				
				IC.Ikariam.viewNames.forEach(function(is_view, ii_index) {
					IC.Options.addSelect(is_view + 'Factor', 'zoom', 'factors', 100, IC.Language.$('zoom.options.factor.' + is_view), la_options, { replace: !!ib_replace });
				});
			};
			
			/**
			 * Add the checkboxes fields for rescaling the badges and icons.
			 */
			var _lf_addScaleChildrenCheckboxes = function() {
				IC.Options.addHTML('scaleChildrenDescription', 'zoom', 'scale', { html: '<p>' + IC.Language.$('zoom.options.scaleChildren.label') + '</p>' });
				
				IC.Ikariam.viewNames.forEach(function(is_view) {
					IC.Options.addCheckbox(is_view + 'ScaleChildren', 'zoom', 'scale', true, IC.Language.$('zoom.options.scaleChildren.' + is_view), {});
				});
			};
			
			/**
			 * Add the checkboxes for the access keys.
			 */
			var _lf_addAccessKeyCheckboxes = function() {
				IC.Options.addHTML('accessKeyDescription', 'zoom', 'accessKeys', { html: '<p>' + IC.Language.$('zoom.options.accessKeyLabel') + '</p>' });
				
				var la_accessKeys = ['ctrl', 'alt', 'shift'];
				la_accessKeys.forEach(function(is_accessKey) {
					var lb_defaultEnabled = (is_accessKey == 'ctrl');
					IC.Options.addCheckbox(is_accessKey + 'AsAccessKey', 'zoom', 'accessKeys', lb_defaultEnabled, IC.Language.$('general.' + is_accessKey), {});
				});
			};
			
			/**
			 * Create the zoom options wrapper and add the option elements.
			 */
			this.create = function() {
				_lf_addFactorSelects();
				_lf_addScaleChildrenCheckboxes();
				_lf_addAccessKeyCheckboxes();
				
				_lb_zoomWrapperCreated = true;
			};
			
			/**
			 * Recreate the select fields for the zoom factor (and keep the old zoom factor).
			 */
			this.renewFactorSelects = function() {
				if(_lb_zoomWrapperCreated === false)
					return;
				
				_lf_addFactorSelects(true);
			};
		};
		
		IC.Options.addWrapper('zoom', IC.Language.$('zoom.options.wrapperTitle'));
		
		// Show missing resources in upgrade / building view.
		IC.Options.addCheckbox('view', 'zoom', 'general', true, IC.Language.$('zoom.options.zoomView'), { changeCallback: _go_zoomView.updateSettings });
		
		// Add the zoom function settings.
		_go_zoomOptions.create();
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: zoom functions created');
	
	// Ressource information.
	(function() {
		/**
		 * Storage for the style functions for the capacity bar and the direct income.
		 * 
		 * @type	{object}
		 */
		var _go_styleFunctions = new function() {
			/**
			 * Get the style for the #js_GlobalMenu_ elements.
			 * 
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getGlobalMenuStyle = function(is_incomeStyle) {
				if(is_incomeStyle != 'alignLeft')
					return '#js_GlobalMenu_wood, #js_GlobalMenu_wine, #js_GlobalMenu_marble, #js_GlobalMenu_crystal, #js_GlobalMenu_sulfur { padding-right: 4px; } ';
				
				return '';
			};
			
			/**
			 * Gets the style for the separation of the resource values.
			 * 
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getSeparationStyle = function(is_incomeStyle) {
				if(is_incomeStyle == 'withSeparation')
					return '#resources_wood, #resources_wine, #resources_marble, #resources_glass { border-right: 1px dotted #542C0F; } ';
				
				return '';
			};
			
			/**
			 * Get styles which are independent of the activation of the cpacity bar or direct income.
			 * 
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getGeneralStyles = function(is_incomeStyle) {
				var ls_globalMenuStyle = _lf_getGlobalMenuStyle(is_incomeStyle);
				var ls_separationStyle	= _lf_getSeparationStyle(is_incomeStyle);
				
				return ls_globalMenuStyle + ls_separationStyle;
			};
			
			/**
			 * Get the style for the capacity bar "wrapper".
			 * 
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getCapacityBarStyle = function(is_capacityBarOrientation, is_incomeStyle) {
				var ls_height		= 'height: 4px; ';
				var ls_width		= 'width: 79px;';
				var ls_right		= 'right: 4px; ';
				var ls_marginLeft	= '';
				
				if(is_capacityBarOrientation == 'horizontal')
					ls_width = 'width: 50px; ';
				
				if(is_capacityBarOrientation == 'vertical') {
					ls_height	= 'height: 21px; ';
					ls_width	= 'width: 4px; ';
					ls_right	= '';
					
					if(is_incomeStyle == 'alignLeft')
						ls_marginLeft = 'margin-left: -7px; ';
				}
				
				return '.' + IC.myGM.prefix + 'capacityInformation { position: absolute; bottom: 4px; ' + ls_height + ls_width + ls_right + ls_marginLeft + '} ';
			};
			
			/**
			 * Get the style for the capacity bar with a border.
			 * 
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getCapacityBarWithBorderStyle = function(is_capacityBarOrientation) {
				var ls_height	= 'height: 3px; ';
				var ls_width	= 'width: 78px; ';
				var ls_right	= 'right: 3px; ';
				
				if(is_capacityBarOrientation == 'horizontal')
					ls_width	= 'width: 50px; ';
				
				if(is_capacityBarOrientation == 'vertical') {
					ls_height	= 'height: 20px; ';
					ls_width	= 'width: 3px; ';
					ls_right	= '';
				}
				
				return '.' + IC.myGM.prefix + 'capacityInformation.' + IC.myGM.prefix + 'border { border: 1px inset #906646; bottom: 3px; ' + ls_height + ls_width + ls_right + '} ';
			};
			
			/**
			 * Get the style for the capacity bar.
			 * 
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getCapacityStyle = function(is_capacityBarOrientation, is_incomeStyle) {
				var ls_prefix = IC.myGM.prefix;
				
				var ls_barStyle =	'.' + ls_prefix + 'bar							{ height: 100%; width: 100%; bottom: 0px; position: absolute; } \
									 .' + ls_prefix + 'bar.' + ls_prefix + 'red		{ background-color: #AA0000; } \
									 .' + ls_prefix + 'bar.' + ls_prefix + 'yellow	{ background-color: #FFD700; } \
									 .' + ls_prefix + 'bar.' + ls_prefix + 'green	{ background-color: #669900; }';
				
				var ls_capacityStyle		= _lf_getCapacityBarStyle(is_capacityBarOrientation, is_incomeStyle);
				var ls_capacityBorderStyle	= _lf_getCapacityBarWithBorderStyle(is_capacityBarOrientation);
				
				return ls_barStyle + ls_capacityStyle + ls_capacityBorderStyle;
			};
			
			/**
			 * Get the style for the direct hourly income in the town view.
			 * 
			 * @param	{boolean}	ib_capacityBarActive
			 *   If the capacity bar is shown.
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 *   
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getHourlyIncomeStyle = function(ib_capacityBarActive, is_capacityBarOrientation, is_incomeStyle) {
				var ls_display		= 'display: block; ';
				var ls_fontSize		= 'font-size: 11px; ';
				var ls_paddingRight	= 'padding-right: 4px; ';
				
				if(ib_capacityBarActive === true && is_capacityBarOrientation !== 'horizontalFull')
					ls_fontSize = 'font-size: 9px; ';
				
				if(is_incomeStyle == 'alignLeft')
					ls_paddingRight = '';
				
				return '.' + IC.myGM.prefix + 'hourlyIncomeResource { ' + ls_display + ls_fontSize + ls_paddingRight + '} ';
			};
			
			/**
			 * Get some resource styles which are only set if the capacity bar is active.
			 * 
			 * @param	{boolean}	ib_directIncomeActive
			 *   If the direct income is shown.
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 * 
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getResourceStyleCapacityActive = function(ib_directIncomeActive, is_capacityBarOrientation, is_incomeStyle) {
				var ls_height		= '';
				var ls_top			= '';
				var ls_paddingLeft	= '';
				var ls_fontSize		= '';
				
				if(is_capacityBarOrientation == 'horizontalFull') {
					ls_height	= 'height: 32px !important; ';
					ls_top		= 'top: -2px !important; ';
				}
				
				if(is_capacityBarOrientation == 'vertical' && is_incomeStyle == 'alignLeft')
					ls_paddingLeft = 'padding-left: 38px !important; ';
				
				if(is_capacityBarOrientation == 'vertical' || (ib_directIncomeActive === true && is_capacityBarOrientation != 'horizontalFull'))
					ls_fontSize = 'font-size: 11px; ';
				
				return ls_height + ls_top + ls_paddingLeft + ls_fontSize;
			};
			
			/**
			 * Get the line height for the sored resources.
			 * 
			 * @param	{boolean}	ib_capacityBarActive
			 *   If the capacity bar is shown.
			 * @param	{boolean}	ib_directIncomeActive
			 *   If the direct income is shown.
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 *   
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getResourceStyleLineHeigth = function(ib_capacityBarActive, ib_directIncomeActive, is_capacityBarOrientation) {
				if(ib_directIncomeActive === true) {
					if(ib_capacityBarActive === true && is_capacityBarOrientation == 'horizontal')
						return 'line-height: 9px !important; ';
					
					return 'line-height: 11px !important; ';
				}
				
				if(is_capacityBarOrientation == 'horizontal')
					return 'line-height: 12px !important; ';
				
				if(is_capacityBarOrientation == 'vertical')
					return 'line-height: 24px !important; ';
				
				return '';
			};
			
			/**
			 * Get the style for the resource fields.
			 * 
			 * @param	{boolean}	ib_capacityBarActive
			 *   If the capacity bar is shown.
			 * @param	{boolean}	ib_directIncomeActive
			 *   If the direct income is shown.
			 * @param	{string}	is_capacityBarOrientation
			 *   The orientation of the capacity bar.
			 * @param	{string}	is_incomeStyle
			 *   How the income is styled.
			 *   
			 * @return	{string}
			 *   The style string.
			 */
			var _lf_getResourceStyle = function(ib_capacityBarActive, ib_directIncomeActive, is_capacityBarOrientation, is_incomeStyle) {
				var ls_align			= 'text-align: right; ';
				var ls_capacityActive	= '';
				var ls_lineHeight		= _lf_getResourceStyleLineHeigth(ib_capacityBarActive, ib_directIncomeActive, is_capacityBarOrientation);
				
				if(is_incomeStyle == 'alignLeft')
					ls_align = '';
				
				if(ib_capacityBarActive === true)
					ls_capacityActive = _lf_getResourceStyleCapacityActive(ib_directIncomeActive, is_capacityBarOrientation, is_incomeStyle);
				
				return '#resources_wood, #resources_wine, #resources_marble, #resources_glass, #resources_sulfur { ' + ls_align + ls_lineHeight + ls_capacityActive + ' } ';
			};
			
			/**
			 * Set the styles for the direct income and the capacity bar.
			 */
			this.setStyles = function() {
				var lb_capacityBarActive = IC.Options.getOption('resourceInformation', 'showCapacityBar');
				var lb_directIncomeActive = IC.Options.getOption('resourceInformation', 'showDirectIncome');
				
				// If not both of the checkboxes have been defined yet, do nothing!
				if(lb_capacityBarActive === null || lb_directIncomeActive === null)
					return;
				
				if(lb_capacityBarActive === false && lb_directIncomeActive === false) {
					IC.myGM.removeStyle('resourceInformation');
					return;
				}
				
				var ls_capacityBarOrientation	= IC.Options.getOption('resourceInformation', 'capacityBarOrientation');
				var ls_incomeStyle				= IC.Options.getOption('resourceInformation', 'incomeStyle');
				
				var ls_incomeStyleString = _lf_getGeneralStyles(ls_incomeStyle)
												+ _lf_getResourceStyle(lb_capacityBarActive, lb_directIncomeActive, ls_capacityBarOrientation, ls_incomeStyle);
				
				if(lb_capacityBarActive === true)
					ls_incomeStyleString += _lf_getCapacityStyle(ls_capacityBarOrientation, ls_incomeStyle);
				
				if(lb_directIncomeActive === true)
					ls_incomeStyleString += _lf_getHourlyIncomeStyle(lb_capacityBarActive, ls_capacityBarOrientation, ls_incomeStyle);
				
				IC.myGM.addStyle(ls_incomeStyleString, 'resourceInformation', true);
			};
		};
		
		/**
		 * Storage for the functions for the capacity bar.
		 * 
		 * @type	{object}
		 */
		var _go_capacityInformation = new function() {
			/**
			 * Update the capacity information bars.
			 */
			var _lf_updateFields = function() {
				if(IC.ika.model.isOwnCity === false)
					return;
				
				var la_capacityInformation = [];
				var la_resourceIdentifiers = ['resource', 1, 2, 3, 4];
				
				la_resourceIdentifiers.forEach(function(im_identifier, ii_index) {
					la_capacityInformation.push({
						warehouse:		IC.Ikariam.getInt(IC.ika.getModel().maxResources[im_identifier]),
						branchOffice:	IC.Ikariam.getInt(IC.ika.getModel().branchOfficeResources[im_identifier]),
						current:		IC.Ikariam.getInt(IC.ika.getModel().currentResources[im_identifier])
					});
				});
				
				var ls_prefix			= '#' + IC.myGM.prefix;
				var ls_styleToChange	= 'width';
				if(IC.Options.getOption('resourceInformation', 'capacityBarOrientation') == 'vertical')
					ls_styleToChange = 'height';
				
				la_capacityInformation.forEach(function(io_capacity, ii_index) {
					var ls_resource = IC.Ikariam.resourceNames[ii_index];
					
					var li_warehousePercentage	= 100;
					var li_resourcePercentage	= io_capacity.current / io_capacity.warehouse * 100;
					
					if(IC.Options.getOption('resourceInformation', 'capacityBarShowBranchOfficeResources') === true) {
						var li_maximumCapacity	= io_capacity.warehouse + io_capacity.branchOffice;
						li_warehousePercentage	= io_capacity.warehouse / li_maximumCapacity * 100;
						li_resourcePercentage	= io_capacity.current / li_maximumCapacity * 100;
					}
					
					IC.myGM.$(ls_prefix + 'maxCapacity' + ls_resource).style[ls_styleToChange]			= '100%';
					IC.myGM.$(ls_prefix + 'warehouseCapacity' + ls_resource).style[ls_styleToChange]	= li_warehousePercentage + '%';
					IC.myGM.$(ls_prefix + 'currentResource' + ls_resource).style[ls_styleToChange]		= li_resourcePercentage + '%';
				});
			};
			
			/**
			 * Add the capacity information bars.
			 */
			var _lf_addFields = function() {
				var la_classes	= ['capacityInformation'];
				if(IC.Options.getOption('resourceInformation', 'capacityBarHasBorder') === true)
					la_classes.push('border');
				
				IC.Ikariam.resourceNames.forEach(function(is_resource) {
					var le_wrapper = IC.myGM.addElement('div', IC.myGM.$('#resources_' + is_resource), { 'id': 'capacityInfo' + is_resource, 'classes': la_classes }, true);
					IC.myGM.addElement('div', le_wrapper, { 'id': 'maxCapacity' + is_resource, 'classes': ['bar', 'yellow'] }, true);
					IC.myGM.addElement('div', le_wrapper, { 'id': 'warehouseCapacity' + is_resource, 'classes': ['bar', 'green'] }, true);
					IC.myGM.addElement('div', le_wrapper, { 'id': 'currentResource' + is_resource, 'classes': ['bar', 'red'] }, true);
				});
				
				_lf_updateFields();
			};
			
			/**
			 * Remove the capacity information bars.
			 */
			var _lf_removeFields = function() {
				IC.myGM.removeElement(IC.myGM.$$('.' + IC.myGM.prefix + 'capacityInformation'));
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showCapacityBar
			 *   If the user selected the checkbox to show the capacity information.
			 */
			this.updateSettings = function(ib_showCapacityBar) {
				_go_styleFunctions.setStyles();
				
				if(ib_showCapacityBar === true) {
					_lf_addFields();
					IC.Observer.add('updateCapacityBar', IC.myGM.$('#cityResources'), { childList: true, subtree: true }, _lf_updateFields, _lf_updateFields);
					return;
				}
				
				IC.Observer.remove('updateCapacityBar');
				_lf_removeFields();
			};
		};
		
		/**
		 * Storage for the functions for the direct income.
		 * 
		 * @type	{object}
		 */
		var _go_directIncome = new function() {
			/**
			 * Storage for the tradegood of the last town selected.
			 * 
			 * @type	{int}
			 */
			var _li_lastTradegood = null;
			
			/**
			 * Delete the data of the tradegood of the last town selected.
			 * 
			 * @param	{string}	is_hourlyPrefix
			 *   The prefix for the hourly income wrapper selector.
			 * @param	{string}	is_dailyPrefix
			 *   The prefix for the daily income wrapper selector.
			 */
			var _lf_deleteLastTradegoodData = function(is_hourlyPrefix, is_dailyPrefix) {
				IC.myGM.$(is_hourlyPrefix + IC.Ikariam.resourceNames[_li_lastTradegood]).innerHTML	= '';
				IC.myGM.$(is_dailyPrefix + IC.Ikariam.resourceNames[_li_lastTradegood]).innerHTML	= '';
				
				if(_li_lastTradegood !== 1) {
					IC.myGM.$(is_hourlyPrefix + IC.Ikariam.resourceNames[_li_lastTradegood]).classList.add('invisible');
					IC.myGM.$(is_dailyPrefix + 'Wrapper' + IC.Ikariam.resourceNames[_li_lastTradegood]).classList.add('invisible');
				}
			};
			
			/**
			 * Update the direct icome fields.
			 * 
			 * @param	{boolean}	ib_firstRun
			 *   If this is the first run after adding the fields.
			 */
			var _lf_updateFields = function(ib_firstRun) {
				if(IC.ika.model.isOwnCity === false)
					return;
				
				var ls_hourlyPrefix	= '#' + IC.myGM.prefix + 'hourlyIncomeResource';
				var ls_dailyPrefix	= '#' + IC.myGM.prefix + 'dailyIncomeResource';
				
				if(_li_lastTradegood !== null)
					_lf_deleteLastTradegoodData(ls_hourlyPrefix, ls_dailyPrefix);
				
				var li_producedTradegood	= IC.Ikariam.getInt(IC.ika.getModel().producedTradegood);
				var li_tradegoodProduction	= IC.ika.getModel().tradegoodProduction * 3600 + 0.001;
				var li_woodProduction		= IC.ika.getModel().resourceProduction * 3600 + 0.001;
				var li_wineSpending			= IC.ika.getModel().wineSpendings;
				var li_producesWine			= IC.ika.getModel().cityProducesWine;
				var ls_tradegoodName		= IC.Ikariam.resourceNames[li_producedTradegood];
				
				IC.myGM.$(ls_hourlyPrefix + IC.Ikariam.resourceNames[0]).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(li_woodProduction), true, true);
				IC.myGM.$(ls_dailyPrefix + IC.Ikariam.resourceNames[0]).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(li_woodProduction * 24), false);
				
				IC.myGM.$(ls_hourlyPrefix + IC.Ikariam.resourceNames[1]).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(-1 * li_wineSpending), true, true);
				IC.myGM.$(ls_dailyPrefix + IC.Ikariam.resourceNames[1]).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(-1 * li_wineSpending * 24), false);
				
				if(li_producesWine === true)
					li_tradegoodProduction = li_tradegoodProduction - li_wineSpending;
				
				IC.myGM.$(ls_hourlyPrefix + ls_tradegoodName).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(li_tradegoodProduction), true, true);
				IC.myGM.$(ls_dailyPrefix + ls_tradegoodName).innerHTML	= IC.Ikariam.formatToIkaNumber(Math.floor(li_tradegoodProduction * 24), false);
				
				IC.myGM.$(ls_hourlyPrefix + ls_tradegoodName).classList.remove('invisible');
				IC.myGM.$(ls_dailyPrefix + 'Wrapper' + ls_tradegoodName).classList.remove('invisible');
				
				_li_lastTradegood = li_producedTradegood;
			};
			
			/**
			 * Add the direct income fields.
			 */
			var _lf_addFields = function() {
				IC.Ikariam.resourceNames.forEach(function(is_resource, ii_index) {
					var la_hourlyClasses	= [IC.myGM.prefix + 'hourlyIncomeResource'];
					var la_dailyClasses		= ['smallFont', IC.myGM.prefix + 'dailyIncomeResourceWrapper'];
					
					if(ii_index >= 2) {
						la_hourlyClasses.push('invisible');
						la_dailyClasses.push('invisible');
					}
					
					IC.myGM.addElement('span', IC.myGM.$('#resources_' + is_resource), { 'id': 'hourlyIncomeResource' + is_resource, 'classes': la_hourlyClasses });
					
					var le_dailyIncomeParent	= IC.myGM.$('#resources_' + is_resource + ' .tooltip');
					var le_dailyIncomeWrapper	= IC.myGM.addElement('p', le_dailyIncomeParent, {
						'id':			'dailyIncomeResourceWrapper' + is_resource,
						'classes':		la_dailyClasses,
						'innerHTML':	IC.Language.$('resourceInformation.dailyProduction', [IC.Language.$('diverse.name.resource.' + is_resource)]) + ' '
					}, null, IC.myGM.$('p:nth-child(2)', le_dailyIncomeParent));
					
					IC.myGM.addElement('span', le_dailyIncomeWrapper, { 'id': 'dailyIncomeResource' + is_resource });
				});
				
				_lf_updateFields(true);
			};
			
			/**
			 * Remove the direct income fields.
			 */
			var _lf_removeFields = function() {
				IC.myGM.removeElement(IC.myGM.$$('.' + IC.myGM.prefix + 'hourlyIncomeResource' + ', .' + IC.myGM.prefix + 'dailyIncomeResourceWrapper'));
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showDirectIncome
			 *   If the user selected the checkbox to show the income directly in town view.
			 */
			this.updateSettings = function(ib_showDirectIncome) {
				_go_styleFunctions.setStyles();
				
				if(ib_showDirectIncome === true) {
					_lf_addFields();
					IC.RefreshHandler.add('*', 'directIncome', _lf_updateFields);
					return;
				}
				
				IC.RefreshHandler.remove('*', 'directIncome');
				_lf_removeFields();
			};
		};
		
		/**
		 * Storage for the functions for the resource quicklinks enhancements.
		 * 
		 * @type	{object}
		 */
		var _go_resourceQuicklinkEnhancements = new function() {
			/**
			 * Open the town hall of the selected town.
			 */
			var _lf_openTownHall = function() {
				var ls_selectedCity	= IC.ika.getModel().relatedCityData.selectedCity;
				var ls_cityId		= IC.ika.getModel().relatedCityData[ls_selectedCity].id;
				
				if(IC.Ikariam.view == 'town') {
					IC.win.ajaxHandlerCall('?view=townHall&cityId=' + ls_cityId + '&position=0');
					
					return;
				}
				
				IC.win.ajaxHandlerCall('?view=city&dialog=townHall&cityId=' + ls_cityId + '&position=0');
			};
			
			/**
			 * Update the css to hover only the resources which are clickable.
			 */
			var _lf_updateCSS = function() {
				var ls_activeResource = IC.Ikariam.resourceNames[IC.ika.getModel().producedTradegood];
				
				IC.myGM.addStyle(
					'#resources_population:hover, #resources_wood:hover, #resources_' + ls_activeResource + ':hover	{ text-shadow: 2px 2px 2px #666; cursor: pointer; color: #333; }',
					'resourceQuicklinkEnhancements', true
				);
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_enhanceResourceQuicklinks
			 *   If the user selected the checkbox to enhance the resource quicklinks.
			 */
			this.updateSettings = function(ib_enhanceResourceQuicklinks) {
				if(ib_enhanceResourceQuicklinks === true) {
					IC.myGM.$('#resources_population').addEventListener('click', _lf_openTownHall, true);
					IC.RefreshHandler.add('*', 'resourceQuicklinkEnhancements', _lf_updateCSS);
					return;
				}
				
				IC.RefreshHandler.remove('*', 'resourceQuicklinkEnhancements');
				IC.myGM.$('#resources_population').removeEventListener('click', _lf_openTownHall, true);
				IC.myGM.removeStyle('resourceQuicklinkEnhancements');
			};
		};
		
		IC.Options.addWrapper('resourceInformation', IC.Language.$('resourceInformation.options.wrapperTitle'));
		
		// Option to show the income directly in town view.
		IC.Options.addCheckbox('showDirectIncome', 'resourceInformation', 1, true, IC.Language.$('resourceInformation.options.directIncome.show'), { changeCallback: _go_directIncome.updateSettings });
		// Option to enhance the resource quicklinks and add one to population.
		IC.Options.addCheckbox('resourceQuicklinkEnhancements', 'resourceInformation', 1, true, IC.Language.$('resourceInformation.options.resourceQuicklinkEnhancements'), { changeCallback: _go_resourceQuicklinkEnhancements.updateSettings });
		
		// Option to show the capacity information.
		IC.Options.addCheckbox('showCapacityBar', 'resourceInformation', 2, true, IC.Language.$('resourceInformation.options.capacityBar.show'), { changeCallback: _go_capacityInformation.updateSettings });
		// Option to show the capacity bar with border.
		IC.Options.addCheckbox('capacityBarHasBorder', 'resourceInformation', 2, true, IC.Language.$('resourceInformation.options.capacityBar.hasBorder'), {});
		// Option to show also branch office ressources.
		IC.Options.addCheckbox('capacityBarShowBranchOfficeResources', 'resourceInformation', 2, true, IC.Language.$('resourceInformation.options.capacityBar.showBranchOfficeResources'), {});
		
		// Style of the resources in warehouse / daily income.
		IC.Options.addSelect('incomeStyle', 'resourceInformation', 3, 'alignRight', IC.Language.$('resourceInformation.options.directIncome.style.label'), [
			{ value: 'alignRight',		label: IC.Language.$('resourceInformation.options.directIncome.style.alignRight') },
			{ value: 'alignLeft',		label: IC.Language.$('resourceInformation.options.directIncome.style.alignLeft') },
			{ value: 'withSeparation',	label: IC.Language.$('resourceInformation.options.directIncome.style.withSeparation') }
		], {});
		// Style of the capacity bar.
		IC.Options.addSelect('capacityBarOrientation', 'resourceInformation', 3, 'vertical', IC.Language.$('resourceInformation.options.capacityBar.orientation.label'), [
			{ value: 'vertical',		label: IC.Language.$('resourceInformation.options.capacityBar.orientation.vertical') },
			{ value: 'horizontal',		label: IC.Language.$('resourceInformation.options.capacityBar.orientation.horizontal') },
			{ value: 'horizontalFull',	label: IC.Language.$('resourceInformation.options.capacityBar.orientation.horizontalFull') }
		], {});
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: resource information functions created');
	
	// Member information in highscore view.
	(function() {
		/**
		 * Storage for the member information functions.
		 * 
		 * @type	{object}
		 */
		var _go_memberInformation = new function() {
			/**
			 * Storage for the member data.
			 * 
			 * @type	{object}
			 */
			var _lo_data		= null;
			
			/**
			 * Key for the storage of the member information.
			 * 
			 * @type	{string}
			 */
			var _ls_dataKey	= '';
			
			/**
			 * Key for the storage of the last reset date.
			 * 
			 * @type	{string}
			 */
			var _ls_timeKey	= '';
			
			/**
			 * Show the data.
			 */
			var _lf_clickShow = function() {
				IC.myGM.setValue('memberInfo_infoLinkClicked', true);
				
				IC.myGM.$('#tab_highscore input[name="searchUser"]').value	= '';
				IC.myGM.$('#searchOnlyFriends').checked						= false;
				IC.myGM.$('#searchOnlyAllies').checked						= true;
				
				IC.myGM.$('#tab_highscore input[type="submit"]').click();
			};
			
			/**
			 * Reset the stored data.
			 */
			var _lf_clickReset = function() {
				IC.myGM.setValue(_ls_dataKey, _lo_data);
				IC.myGM.setValue(_ls_timeKey, (new Date).getTime());
				
				_lf_clickShow();
			};
			
			/**
			 * Add the button to show the data.
			 */
			var _lf_addShowButton = function() {
				var le_button = IC.myGM.addButton(IC.myGM.$('#tab_highscore .centerButton'), IC.Language.$('highscore.memberInformation.show'), _lf_clickShow, true);
				le_button.id = IC.myGM.prefix + 'showInfo';
			};
			
			/**
			 * Add the button to reset the stored data.
			 */
			var _lf_addResetButton = function() {
				var le_button = IC.myGM.addButton(IC.myGM.$('#tab_highscore .content p'), IC.Language.$('highscore.memberInformation.reset'), _lf_clickReset, true);
				le_button.id = IC.myGM.prefix + 'resetInfo';
			};
			
			/**
			 * Get the own data and show the differences to the last stored data.
			 * 
			 * @param	{object}	io_oldMemberInfo
			 *   The last stored data.
			 * 
			 * @return	{object}
			 *   The own data.
			 */
			var _lf_getOwnDataShowDifference = function(io_oldMemberInfo) {
				var le_ownRow = IC.myGM.$('table.highscore tr.own');
				var ro_ownData = {
					'place': IC.Ikariam.getInt(IC.myGM.$('.place', le_ownRow).innerHTML),
					'score': IC.Ikariam.getInt(IC.myGM.$('.score', le_ownRow).innerHTML)
				};
				
				var ls_placeDifference = '-';
				var ls_scoreDifference = '-';
				if(!!io_oldMemberInfo === true && !!io_oldMemberInfo['own'] === true) {
					ls_placeDifference = IC.Ikariam.formatToIkaNumber(io_oldMemberInfo['own']['place'] - ro_ownData['place'], true, true);
					ls_scoreDifference = IC.Ikariam.formatToIkaNumber(ro_ownData['score'] - io_oldMemberInfo['own']['score'], true, true);
				}
				
				IC.myGM.addElement('span', IC.myGM.$('.place', le_ownRow), { 'innerHTML': ls_placeDifference });
				IC.myGM.addElement('span', IC.myGM.$('.score', le_ownRow), { 'innerHTML': ls_scoreDifference });
				
				return ro_ownData;
			};
			
			/**
			 * Get the data and show the differences to the last stored data.
			 * 
			 * @return	{object}
			 *   The member data.
			 */
			var _lf_getDataShowDifference = function() {
				var ro_memberInfo		= {};
				var lo_oldMemberInfo	= IC.myGM.getValue(_ls_dataKey, null);
				var la_allyMemberRows	= IC.myGM.$$('table.highscore tr.ownally');
				
				la_allyMemberRows.forEach(function(ie_allyMemberRow) {
					var ls_actionLink	= IC.myGM.$('.action a', ie_allyMemberRow).href;
					var ls_memberId		= ls_actionLink.match(/receiverId=([0-9]*)/i)[1];
					
					ro_memberInfo[ls_memberId] = {
						'place': IC.Ikariam.getInt(IC.myGM.$('.place', ie_allyMemberRow).innerHTML),
						'score': IC.Ikariam.getInt(IC.myGM.$('.score', ie_allyMemberRow).innerHTML)
					};
					
					var ls_placeDifference = '-';
					var ls_scoreDifference = '-';
					if(!!lo_oldMemberInfo === true && !!lo_oldMemberInfo[ls_memberId] === true) {
						ls_placeDifference = IC.Ikariam.formatToIkaNumber(lo_oldMemberInfo[ls_memberId]['place'] - ro_memberInfo[ls_memberId]['place'], true, true);
						ls_scoreDifference = IC.Ikariam.formatToIkaNumber(ro_memberInfo[ls_memberId]['score'] - lo_oldMemberInfo[ls_memberId]['score'], true, true);
					}
					
					IC.myGM.addElement('span', IC.myGM.$('.place', ie_allyMemberRow), { 'innerHTML': ls_placeDifference });
					IC.myGM.addElement('span', IC.myGM.$('.score', ie_allyMemberRow), { 'innerHTML': ls_scoreDifference });
				});
				
				ro_memberInfo['own'] = _lf_getOwnDataShowDifference(lo_oldMemberInfo);
				
				return ro_memberInfo;
			};
			
			/**
			 * Add the span with the time of the last reset.
			 */
			var _lf_addLastResetTime = function() {
				var li_lastResetTime	= IC.myGM.getValue(_ls_timeKey, 0);
				var li_differenceInSec	= ((new Date()).getTime() - li_lastResetTime) / 1000;
				var ls_lastReset		= IC.Language.$('highscore.memberInformation.noReset');
				
				if(li_lastResetTime > 0) {
					var li_days	= Math.floor(li_differenceInSec / 86400);
					var li_hours	= Math.floor(li_differenceInSec / 3600) % 24;
					var li_minutes	= Math.floor(li_differenceInSec / 60) % 60;
					
					ls_lastReset = li_days + 'd ' + li_hours + 'h ' + li_minutes + 'min';
				}
				
				IC.myGM.addElement('br', IC.myGM.$('#tab_highscore .content p'));
				IC.myGM.addElement('span', IC.myGM.$('#tab_highscore .content p'), {
					'classes':		['bold', 'brown'],
					'innerHTML':	IC.Language.$('highscore.memberInformation.lastReset', [ls_lastReset])
				});
			};
			
			/**
			 * Prepare the highscore popup to show the data and show the data if requested.
			 */
			var _lf_doPreparePopup = function() {
				_ls_dataKey = IC.Ikariam.serverCode + '_' + IC.ika.getModel().avatarAllyId + '_memberInfo_data_' + IC.myGM.getSelectValue('js_highscoreType', true, true);
				_ls_timeKey = IC.Ikariam.serverCode + '_' + IC.ika.getModel().avatarAllyId + '_memberInfo_time_' + IC.myGM.getSelectValue('js_highscoreType', true, true);
				
				_lf_addShowButton();
				
				if(IC.myGM.getValue('memberInfo_infoLinkClicked', false) === true) {
					IC.myGM.deleteValue('memberInfo_infoLinkClicked');
					
					_lo_data = _lf_getDataShowDifference();
					_lf_addResetButton();
					_lf_addLastResetTime();
				}
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_showMemberInformation
			 *   If the user selected the checkbox to show the member information.
			 */
			this.updateSettings = function(ib_showMemberInformation) {
				if(ib_showMemberInformation === true) {
					IC.RefreshHandler.add('highscore', 'memberInformation', _lf_doPreparePopup);
					IC.myGM.addStyle(
						'#' + IC.myGM.prefix + "resetInfo	{ float: right; margin-top: -6px; margin-right: 6px; } \
						 .highscore .score span				{ float: right; text-align: right; width: 70px; } \
						 .highscore .place span				{ float: right; text-align: right; width: 30px; } \
						 .highscore th:nth-child(4)			{ width: 30% !important; } \
						 .highscore th:nth-child(5)			{ width: 10% !important; } \
						 #tab_highscore	.centerButton		{ margin: 10px 0px; }",
						'memberInformation', true
					);
					return;
				}
				
				IC.RefreshHandler.remove('highscore', 'memberInformation');
				IC.myGM.removeStyle('memberInformation');
			};
		};
		
		// Show the member information.
		IC.Options.addCheckbox('showMemberInformation', 'diverseOptions', 1, false, IC.Language.$('highscore.options.showMemberInformation'), { changeCallback: _go_memberInformation.updateSettings });
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: highscore functions created');
	
	// Message enhancements.
	(function() {
		/**
		 * Storage for the url replacement functions.
		 * 
		 * @type	{object}
		 */
		var _go_replaceURL = new function() {
			/**
			 * Show a warning when a replaced link is clicked.
			 */
			var _lf_showNotification = function() {
				var ls_urlToOpen = this.innerHTML.IC.decodeHTML();
				
				var lo_notificationText = {
					header:		IC.Language.$('message.replacedUrl.notification.header'),
					body:		IC.Language.$('message.replacedUrl.notification.text', ['<span class="bold red">"' + ls_urlToOpen + '"</span>']),
					confirm:	IC.Language.$('general.yes'),
					abort:		IC.Language.$('general.no')
				};
				
				var lo_notificationCallback = {
					confirm:	function() { IC.win.open(ls_urlToOpen); },
					abort:		function() { /* Only set to show the abort button */ }
				};
				
				IC.myGM.notification(lo_notificationText, lo_notificationCallback);
			};
			
			/**
			 * Replace the links.
			 */
			var _lf_doReplace = function() {
				var la_messageBodys = IC.myGM.$$('.msgText');
				
				la_messageBodys.forEach(function(ie_messageBody) {
					var ls_text = ie_messageBody.innerHTML;
					ie_messageBody.innerHTML = ls_text.replace(/(?:^|\s)(http(s?)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,4}(\/[^<\s]*)?)/g, ' <span class="' + IC.myGM.prefix + 'replacedUrl" title="$1">$1</span> ');
				});
				
				var la_replacedURLs = IC.myGM.$$('.' + IC.myGM.prefix + 'replacedUrl');
				
				la_replacedURLs.forEach(function(ie_replacedURL) {
					ie_replacedURL.addEventListener('click', _lf_showNotification, true);
				});
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_replaceURL
			 *   If the user selected the checkbox to replace the url.
			 */
			this.updateSettings = function(ib_replaceURL) {
				var la_postboxes = ['diplomacyAdvisor', 'diplomacyAdvisorOutBox', 'diplomacyAdvisorArchive', 'diplomacyAdvisorArchiveOutBox'];
				if(ib_replaceURL === true) {
					IC.RefreshHandler.add(la_postboxes, 'replaceURL', _lf_doReplace);
					IC.myGM.addStyle(
						'.' + IC.myGM.prefix + 'replacedUrl			{ font-weight: bold; font-style: italic; } \
						 .' + IC.myGM.prefix + 'replacedUrl:hover	{ text-decoration: underline; cursor: pointer; }',
						'replaceURL', true
					);
					return;
				}
				
				IC.RefreshHandler.remove(la_postboxes, 'replaceURL');
				IC.myGM.removeStyle('replaceURL');
			};
		};
		
		/**
		 * Storage for the message signature functions.
		 * 
		 * @type	{object}
		 */
		var _go_messageSignature = new function() {
			/**
			 * Add the signature to a new message.
			 */
			var _lf_addSignature = function() {
				var ls_signature = IC.Options.getOption('messages', 'globalSignature');
				
				if(IC.Options.getOption('messages', 'useServerSignature') === true)
					ls_signature = IC.Options.getOption('messages', 'serverSignature');
				
				if(ls_signature === '')
					return;
				
				var le_textarea = IC.myGM.$('#js_msgTextConfirm');
				
				var ls_text = le_textarea.value;
				
				if(IC.Options.getOption('messages', 'placementTop'))
					ls_text = '\n\n' + ls_signature + ls_text;
				else
					ls_text = ls_text + '\n\n' + ls_signature;
				
				le_textarea.value = ls_text;
				
				le_textarea.setSelectionRange(0,0);
				le_textarea.focus();
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_addMessageSignature
			 *   If the user selected the checkbox to add signatures to messages.
			 */
			this.updateSettings = function(ib_addMessageSignature) {
				if(ib_addMessageSignature === true) {
					IC.RefreshHandler.add('sendIKMessage', 'addMessageSignature', _lf_addSignature);
					return;
				}
				
				IC.RefreshHandler.remove('sendIKMessage', 'addMessageSignature');
			};
		};
		
		/**
		 * Storage for the easy circular message link functions.
		 * 
		 * @type	{object}
		 */
		var _go_easyCircularMessageLink = new function() {
			/**
			 * Add the circular message link and style.
			 */
			var _lf_addLink = function() {
				if(IC.ika.getModel().hasAlly !== true)
					return;
				
				// Add the message link (workaround for ajaxHandlerCall).
				var ls_id		= IC.myGM.prefix + 'circularMessageLink';
				var ls_href		= '?view=sendIKMessage&msgType=51&allyId=' + IC.ika.getModel().avatarAllyId;
				var ls_title	= IC.Language.$('message.easyCircular.send');
				IC.myGM.addElement('div', ge_toolbar, {
					'id':			'circularMessageLinkWrapper',
					'innerHTML':	'<a id="' + ls_id + '" href="' + ls_href + '" title="' + ls_title + '" onclick="ajaxHandlerCall(this.href); return false;"></a>'
				});
				
				IC.myGM.addStyle(
					'#' + IC.myGM.prefix + 'circularMessageLink			{ height: 9px; width: 13px; margin: 0px !important; background: url("skin/interface/icon_send_message.png") repeat scroll 0 0 transparent; }\
					 #' + IC.myGM.prefix + 'circularMessageLink:hover	{ background-position: 0px -9px; }',
					'easyCircularMessage', true
				);
			};
			
			/**
			 * Remove the circular message link and style.
			 */
			var _lf_removeLink = function() {
				IC.myGM.removeElement(IC.myGM.$('#' + IC.myGM.prefix + 'circularMessageLinkWrapper'));
				IC.myGM.removeStyle('easyCircularMessage');
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_addEasyCircularMessageLink
			 *   If the user selected the checkbox to add signatures to messages.
			 */
			this.updateSettings = function(ib_addEasyCircularMessageLink) {
				if(ib_addEasyCircularMessageLink === true) {
					_lf_addLink();
					return;
				}
				
				_lf_removeLink();
			};
		};
		
		IC.Options.addWrapper('messages', IC.Language.$('message.options.wrapperTitle'));
		
		// Replace urls.
		IC.Options.addCheckbox('replaceURL', 'messages', 1, true, IC.Language.$('message.options.replaceURL'), { changeCallback: _go_replaceURL.updateSettings });
		// Provide link for easy circular messages.
		IC.Options.addCheckbox('easyCircularMessage', 'messages', 1, true, IC.Language.$('message.options.easyCircularMessage'), { changeCallback: _go_easyCircularMessageLink.updateSettings });
		
		// Add a signature.
		IC.Options.addCheckbox('messageSignature', 'messages', 2, true, IC.Language.$('message.options.signature.use'), { changeCallback: _go_messageSignature.updateSettings });
		// Place the signature on top.
		IC.Options.addCheckbox('signaturePlacementTop', 'messages', 2, true, IC.Language.$('message.options.signature.placementTop'), {});
		
		// Define a global signature.
		IC.Options.addTextArea('globalSignature', 'messages', 3, '', IC.Language.$('message.options.signature.global'), {});
		
		// Use the server signature.
		IC.Options.addCheckbox('useServerSignature', 'messages', 4, false, IC.Language.$('message.options.signature.useServerSpecific'), { 'serverSpecific': true });
		// Define a server specific signature.
		IC.Options.addTextArea('serverSignature', 'messages', 4, '', IC.Language.$('message.options.signature.server'), { 'serverSpecific': true });
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: message functions created');
	
	// Troop information.
	(function() {
		/**
		 * Provider for data storages.
		 * 
		 * @type	{object}
		 */
		var _go_storageProvider = new function() {
			/**
			 * Constructor for troop list.
			 */
			var _lf_troopList = function() {
				/**
				 * Storage for troop information.
				 * 
				 * @type	{lf_troop[]}
				 */
				var la_troopList = [];
				
				/**
				 * Troop information storage.
				 * 
				 * @param	{string}	is_name
				 *   The name of the troop.
				 * @param	{int}		ii_number
				 *   The number of the troop.
				 */
				var lf_troop = function(is_name, ii_number) {
					var ls_name		= is_name;
					var li_number	= ii_number;
					
					this.toString = function() {
						return '\n' + ls_name + ': ' + li_number;
					};
				};
				
				/**
				 * If the troop list contains no entries.
				 * 
				 * @return	{boolean}
				 *   If the troop list is empty.
				 */
				this.__defineGetter__('isEmpty', function() {
					return la_troopList.length < 1;
				});
				
				/**
				 * Add a new troop to the troop list.
				 * 
				 * @param	{string}	is_name
				 *   The name of the troop.
				 * @param	{int}		ii_number
				 *   The number of the troop.
				 */
				this.addTroop = function(is_name, ii_number) {
					la_troopList.push(new lf_troop(is_name, ii_number));
				};
				
				/**
				 * Transform the troop list to a string.
				 * 
				 * @return	{string}
				 *   The string representation of the troop list.
				 */
				this.toString = function() {
					return la_troopList.join('');
				};
			};
			
			/**
			 * Constructor for troop list of foreign player.
			 * 
			 * @param	{string}	is_playerName
			 *   The name of the player who owns the troops.
			 */
			var _lf_foreignTroopList = function(is_playerName) {
				/**
				 * The name of the player who owns the troops.
				 * 
				 * @type	{string}
				 */
				var ls_playerName = is_playerName;
				
				/**
				 * Storage for troop information.
				 * 
				 * @type	{_lf_troopList[]}
				 */
				var lo_troopList	= new _lf_troopList();
				
				/**
				 * If the troop list contains no entries.
				 * 
				 * @return	{boolean}
				 *   If the troop list is empty.
				 */
				this.__defineGetter__('isEmpty', function() {
					return lo_troopList.isEmpty;
				});
				
				/**
				 * Add a new troop to the troop list.
				 * 
				 * @param	{string}	is_name
				 *   The name of the troop.
				 * @param	{int}		ii_number
				 *   The number of the troop.
				 */
				this.addTroop = function(is_name, ii_number) {
					lo_troopList.addTroop(is_name, ii_number);
				};
				
				/**
				 * Transform the troop list to a string.
				 * 
				 * @return	{string}
				 *   The string representation of the troop list.
				 */
				this.toString = function() {
					return '\n* ' + ls_playerName + ' *' + lo_troopList;
				};
			};
			
			/**
			 * Storage for multiple troop lists.
			 * 
			 * @param	{string}	is_type
			 *   The type of the troops (units / ships)
			 * @param	{string}	is_status
			 *   The status of the troops (own / friends / enemies)
			 */
			var _lf_troopListStorage = function(is_type, is_status) {
				/**
				 * The type of the troops (units / ships).
				 * 
				 * @type	{string}
				 */
				var ls_type = is_type;
				
				/**
				 * The status of the troops (own / friends / enemies).
				 * 
				 * @type	{string}
				 */
				var ls_status = is_status;
				
				/**
				 * The troop lists.
				 * 
				 * @type	{(_lf_troopList||_lf_foreignTroopList)[]}
				 */
				var la_troopLists = [];
				
				/**
				 * The status of the troop lists (own / friends / enemies).
				 * 
				 * @return	{boolean}
				 *   The status of the lists.
				 */
				this.__defineGetter__('status', function() {
					return ls_status;
				});
				
				/**
				 * Add a new troop list to the storage.
				 * 
				 * @param	{_lf_troopList||_lf_foreignTroopList}	io_troopList
				 *   The troop list to add.
				 */
				this.addTroopList = function(io_troopList) {
					la_troopLists.push(io_troopList);
				};
				
				/**
				 * Transform the troop list storage to a string.
				 * 
				 * @return	{string}
				 *   The string representation of the troop list storage.
				 */
				this.toString = function() {
					return '\n--- ' + IC.Language.$('troopInformation.' + ls_type + '.' + ls_status) + ' ---' + la_troopLists.join('\n');
				};
			};
			
			/**
			 * Storage for data of all troops of one type.
			 * 
			 * @param	{string}	is_type
			 *   The type of the troops (units / ships)
			 */
			var _lf_troopData = function(is_type) {
				/**
				 * The type of the troops (units / ships).
				 * 
				 * @type	{string}
				 */
				var ls_type = is_type;
				
				/**
				 * The troop list storages.
				 * 
				 * @type	{_lf_troopListStorage[]}
				 */
				var la_listStorages	= [];
				
				/**
				 * If the troop list contains no entries.
				 * 
				 * @return	{boolean}
				 *   If the troop list is empty.
				 */
				this.__defineGetter__('isEmpty', function() {
					return la_listStorages.length < 1;
				});
				
				/**
				 * Add a new troop list to the storage.
				 * 
				 * @param	{string}	is_status
				 *   The status of the troop list.
				 * @param	{_lf_troopList||_lf_foreignTroopList}	io_troopList
				 *   The troop list to add.
				 */
				this.addTroopList = function(is_status, io_troopList) {
					for(var i = 0; i < la_listStorages.length; i++) {
						if(la_listStorages[i].status === is_status) {
							la_listStorages[i].addTroopList(io_troopList);
							return;
						}
					}
					
					var lo_listStorage = new _lf_troopListStorage(ls_type, is_status);
					lo_listStorage.addTroopList(io_troopList);
					la_listStorages.push(lo_listStorage);
				};
				
				/**
				 * Transform the troop data to a string.
				 * 
				 * @param	{string}	is_townInformation
				 *   The information about the town for which the troop data is displayed.
				 * 
				 * @return	{string}
				 *   The string representation of the troop data.
				 */
				this.getString = function(is_townInformation) {
					return '===== ' + IC.Language.$('troopInformation.' + ls_type + '.label', [is_townInformation]) + ' =====' + la_listStorages.join('\n\n');
				};
			};
			
			/**
			 * Get a new troop list representation for own troops.
			 * 
			 * @return	{_lf_troopList}
			 *   The troop list.
			 */
			this.ownTroopList = function() {
				return new _lf_troopList();
			};
			
			/**
			 * Get a new troop list representation for foreign troops.
			 * 
			 * @param	{string}	is_playerName
			 *   The name of the player who owns the troops.
			 * 
			 * @return	{_lf_foreignTroopList}
			 *   The troop list.
			 */
			this.foreignTroopList = function(is_playerName) {
				return new _lf_foreignTroopList(is_playerName);
			};
			
			/**
			 * Get a new troop data storage representation for all troops of one type.
			 * 
			 * @param	{string}	is_type
			 *   The type of the troops (units / ships).
			 * 
			 * @return	{_lf_troopData}
			 *   The troop data storage.
			 */
			this.troopData = function(is_type) {
				return new _lf_troopData(is_type);
			};
		};
		
		/**
		 * Storage for the dta extraction functions.
		 * 
		 * @type	{object}
		 */
		var _go_dataExtractor = new function() {
			/**
			 * Extract own troops from a wrapper.
			 * 
			 * @param	{element}	ie_wrapper
			 *   The wrapper to extract the troops from.
			 * 
			 * @return	{_go_storageProvider.ownTroopList[]}
			 *   The extracted troops.
			 */
			var _lf_extractOwnTroops = function(ie_wrapper) {
				var la_nameCells	= IC.myGM.$$('.table01 .title_img_row th', ie_wrapper);
				var la_numberCells	= IC.myGM.$$('.table01 .count td', ie_wrapper);
				
				var ro_troops = _go_storageProvider.ownTroopList();
				
				for(var i = 0; i < la_nameCells.length; i++) {
					var li_number = IC.Ikariam.getInt(la_numberCells[i].innerHTML);
					
					if(li_number > 0)
						ro_troops.addTroop(la_nameCells[i].title, li_number);
				}
				
				return ro_troops;
			};
			
			/**
			 * Extract foreign troops from a wrapper.
			 * 
			 * @param	{element}	ie_wrapper
			 *   The wrapper to extract the troops from.
			 * 
			 * @return	{_go_storageProvider.foreignTroopList[]}
			 *   The extracted troops.
			 */
			var _lf_extractForeignTroops = function(ie_wrapper) {
				var la_nameCells	= IC.myGM.$$('.table01 .title_img_row th:not(:first-child)', ie_wrapper);
				var la_numberRows	= IC.myGM.$$('.table01 tr:not(.title_img_row)', ie_wrapper);
				
				var li_distance = la_numberRows.length / 2;
				
				var ra_troops = [];
				
				for(var i = 0; i < li_distance; i++) {
					var la_numberCells	= IC.myGM.$$('td:not(:first-child)', la_numberRows[i]).concat(IC.myGM.$$('td:not(:first-child)', la_numberRows[i + li_distance]));
					var ls_playerName	= IC.myGM.$('td a', la_numberRows[i]).innerHTML;
					
					var lo_playerTroops = _go_storageProvider.foreignTroopList(ls_playerName);
				
					for(var i = 0; i < la_nameCells.length; i++) {
						var li_number = IC.Ikariam.getInt(la_numberCells[i].innerHTML);
						
						if(li_number > 0)
							lo_playerTroops.addTroop(la_nameCells[i].title, li_number);
					}
					
					if(lo_playerTroops.isEmpty === false)
						ra_troops.push(lo_playerTroops);
				}
				
				return ra_troops;
			};
			
			/**
			 * Extract all troop of one type.
			 * 
			 * @param	{string}	is_type
			 *   The type of the troops to extract (units / ships).
			 * @param	{string}	is_wrapperTabId
			 *   The id of the tab which contains the wrappers.
			 * 
			 * @return	{_go_storageProvider.troopData}
			 *   The extracted data.
			 */
			var _lf_extractTroops = function(is_type, is_wrapperTabId) {
				var ro_return = _go_storageProvider.troopData(is_type);
				
				var la_wrappers	= IC.myGM.$$('#' + is_wrapperTabId + ' .contentBox01h');
				
				var lo_ownTroops	= _lf_extractOwnTroops(la_wrappers[0]);
				if(lo_ownTroops.isEmpty === false)
					ro_return.addTroopList('own', lo_ownTroops);
				
				var la_foreignStatus = ['friends', 'enemies'];
				
				for(var i = 0; i < la_foreignStatus.length; i++) {
					var la_foreignTroops = _lf_extractForeignTroops(la_wrappers[i + 1]);
					
					for(var j = 0; j < la_foreignTroops.length; j++) {
						ro_return.addTroopList(la_foreignStatus[i], la_foreignTroops[j]);
					}
				}
				
				return ro_return;
			};
			
			/**
			 * Extract all troop from the popup.
			 * 
			 * @return	{object}
			 *   All extracted troops.
			 *   Signature: { units: <_go_storageProvider.troopData>, ships: <_go_storageProvider.troopData> }
			 */
			this.extract = function() {
				return {
					units:	_lf_extractTroops('units', 'tabUnits'),
					ships:	_lf_extractTroops('ships', 'tabShips')
				};
			};
		};
		
		/**
		 * Storage for the troop information functions.
		 * 
		 * @type	{object}
		 */
		var _go_troopInformation = new function() {
			/**
			 * Show the troop information popup.
			 * 
			 * @param	{object}	io_data
			 *   All extracted troop.
			 *   Signature: { units: <_go_storageProvider.troopData>, ships: <_go_storageProvider.troopData> }
			 */
			var _ls_showPopup = function(io_data) {
				var la_output = [];
				var ls_townInformation = function() {
					var lo_allTowns		= IC.ika.getModel().relatedCityData;
					var lo_selectedTown	= lo_allTowns[lo_allTowns.selectedCity];
					
					return lo_selectedTown.name + ' ' + lo_selectedTown.coords;
				};
				
				if(!!io_data === true) {
					if(!!io_data.units.isEmpty === false)
						la_output.push(io_data.units.getString(ls_townInformation));
					if(!!io_data.ships.isEmpty === false)
						la_output.push(io_data.ships.getString(ls_townInformation));
				}
				
				var ls_output = la_output.join('\n\n-------------------------------------------------------------------------------------\n\n');
				
				if(ls_output.length === 0)
					ls_output = IC.Language.$('troopInformation.noTroops', [ls_townInformation]);
				
				var lo_text = {
					header:	IC.Language.$('troopInformation.header', [ls_townInformation]),
					body:	ls_output
				};
				
				IC.myGM.notification(lo_text, null, { textarea: true, readonly: true, autoselect: true });
			};
			
			/**
			 * Extract the data and show the popup.
			 */
			var _lf_showInformation = function() {
				_ls_showPopup(_go_dataExtractor.extract());
			};
			
			/**
			 * Show the troop information link.
			 */
			var _lf_doShowTroopInformationLink = function() {
				var le_button	= IC.myGM.addButton(IC.myGM.$('#cityMilitary_c .buildingDescription'), IC.Language.$('troopInformation.button'), _lf_showInformation, true);
				var la_cssRules	= [['position', 'absolute'], ['top', '5px'], ['right', '20px']];
				
				for(var i = 0; i < la_cssRules.length; i++) {
					le_button.style[la_cssRules[i][0]] = la_cssRules[i][1];
				}
			};
			
			/**
			 * Update the settings to execute the callback or delete the handler.
			 * 
			 * @param	{boolean}	ib_enableTroopInformation
			 *   If the user selected the checkbox to show the troop information.
			 */
			this.updateSettings = function(ib_enableTroopInformation) {
				if(ib_enableTroopInformation === true) {
					IC.RefreshHandler.add('cityMilitary', 'troopInformation', _lf_doShowTroopInformationLink);
					return;
				}
				
				IC.RefreshHandler.remove('cityMilitary', 'troopInformation');
			};
		};
		
		IC.Options.addCheckbox('showTroopInformation', 'diverseOptions', 1, true, IC.Language.$('troopInformation.options.show'), { changeCallback: _go_troopInformation.updateSettings });
	})();
	
	IC.con.timeStamp('IkariamEnhancedUI: troop information functions created');
	IC.con.groupEnd();
}

/**
 * Main function of the script.<br>
 * Inits the Ikariam Core and calls the script functions.<br>
 * Must be called with <pre>setTimeout(main, 0)</pre> for correct setting of some core variables.
 */
function main() {
	// Get the Ikariam core.
	var IC = new IkariamCore('3.0.2', 4369, 'Ikariam Enhanced UI', 'Tobbe', false);
	
	if(IC.myGM.alreadyExecuted === true)
		return;
	
	IC.Language.setDefaultLanguage('en');
	
	IC.Language.addLanguageText('en', {"view": {"options": {"wrapperTitle":"View","moveLoadingCircle":"Move loading circle to position bar","hideBirds":"Hide the bird swarm","noVerticalCenterInTownAdvisor":"Don't center town information in the town advisor"}},"island": {"options": {"showColonizingCityInfo":"Show information about colonizing cities"}},"finance": {"options": {"showIncomeOnTop":"Show income on top in balance view","shortUpkeepReductionTable":"Show a short version of the upkeep reduction"},"income": {"perHour":"Income per hour","perDay":"Income per day","start":"Income without reduction"},"upkeep": {"reason": {"0":"Troops","1":"Ships","2":"Troops & Ships"},"basic":"Basic Costs","supply":"Supply Costs","result":"Total Costs"}},"missingResources": {"options": {"wrapperTitle":"Missing Resources","show":"Show missing resources in construction view","showPositive":"Show also the remaining resources after an upgrade","showColoured":"Show the remaining resources coloured"}},"tooltips": {"options": {"autoshow":"Show tooltips in alliance mebers view and military advisor automatically","showDirectInMilitaryAdvisor":"Show information about cargo / fleets in military view without tooltips"}},"zoom": {"options": {"wrapperTitle":"Zoom function","zoomView":"Activate zoom in world view, island view, town view","factor": {"world":"Zoom worldmap:","island":"Zoom island view:","town":"Zoom town view:"},"scaleChildren": {"label":"Let banners and symbols in normal size when zooming when zooming in this view:","world":"Worldmap","island":"Island view","town":"Town view"},"accessKeyLabel":"This keys must be pressed to zoom with the mouse:"},"zoomIn":"Zoom in","factor":"Zoom factor","zoomOut":"Zoom out"},"resourceInformation": {"options": {"wrapperTitle":"Resource Information","resourceQuicklinkEnhancements":"Link resource number to town hall / mines","directIncome": {"show":"Show the hourly income directly in town view","style": {"label":"Style of the hourly income in town view:","alignRight":"Right align","alignLeft":"Left align","withSeparation":"Right align with separation"}},"capacityBar": {"show":"Show info bar for warehouse capacity","hasBorder":"Has border","showBranchOfficeResources":"Show resources in trading post","orientation": {"label":"Orientation of the bar","vertical":"Vertical","horizontal":"Horizontal","horizontalFull":"Horizontal, full length"}}},"dailyProduction":"Daily production %$1:"},"highscore": {"options": {"showMemberInformation":"Enable the possibility to save highscore data of alliance members"},"memberInformation": {"show":"Alliance info","reset":"Reset","lastReset":"Time since the last reset: %$1","noReset":"No reset so far."}},"message": {"options": {"wrapperTitle":"Messages","replaceURL":"Make links in messages clickable","easyCircularMessage":"Show button for faster sending of circular messages","signature": {"use":"Enable message signatures","placementTop":"Insert signature above cited messages","global":"Global signature, which would be used on every world:","useServerSpecific":"Use local signature","server":"Local signature, which only would be used on this world:"}},"replacedUrl": {"notification": {"header":"Attention!","text":"You're going to open the link %$1. This happens on your own risk. Proceed?"}},"easyCircular": {"send":"Send circular message"}},"troopInformation": {"options": {"show":"Show troop info"},"units": {"label":"Units in %$1","own":"Own units","friends":"Allied units","enemies":"Enemy units"},"ships": {"label":"Ships in %$1","own":"Own ships","friends":"Allied ships","enemies":"Enemy ships"},"button":"Troop information","header":"Troops in %$1","noTroops":"There are no troops in %$1"},"diverse": {"options": {"wrapperTitle":"Diverse"},"name": {"resource": {"gold":"Gold","wood":"Building Material","wine":"Wine","marble":"Marble","glass":"Crystal Glass","sulfur":"Sulphur"},"unit": {"swordsman":"Swordsman","phalanx":"Hoplite","archer":"Archer","marksman":"Sulphur Carabineer","mortar":"Mortar","slinger":"Slinger","catapult":"Catapult","ram":"Battering Ram","steamgiant":"Steam Giant","bombardier":"Balloon-Bombardier","cook":"Cook","medic":"Doctor","girocopter":"Gyrocopter","spearman":"Spearman","spartan":"Spartan"},"ship": {"ballista":"Ballista Ship","catapult":"Catapult Ship","flamethrower":"Fire Ship","mortar":"Mortar Ship","ram":"Ram Ship","steamboat":"Steam Ram","rocketship":"Rocket Ship","submarine":"Diving Boat","paddlespeedship":"Paddle Speedboat","ballooncarrier":"Balloon Carrier","tender":"Tender","transport":"Merchant Ship"}}}});
	
	var la_language = ['de', 'gr', 'it', 'lv', 'ru'];
	for(var i = 0; i < la_language.length; i++) {
		IC.Language.registerLanguageResource(la_language[i], la_language[i], 'http://resources.ikascripts.de/IkariamEnhancedUI/v3.0.2/' + la_language[i] + '.json');
	}
	
	// Instantiate the ui script.
	new EnhancedUI(IC);
}

// Call the main function of the script.
setTimeout(main, 0);