Friday, October 21, 2011

DoA Tools Plus II by LA LARVA & RUNEY (pagina en castellano)


// ==UserScript==
// @name          DoA Power Tools II
// @namespace     http://www.mmogwiki.com/scripts/dragonsofatlantis
// @description   Power Tools for Dragons of Atlantis
// @include       *://apps.facebook.com/dragonsofatlantis/*
// @include       *://*.castle.wonderhill.com/platforms/facebook/game
// @exclude       *://apps.facebook.com/dragonsofatlantis/rubies
// @match         *://apps.facebook.com/dragonsofatlantis/*
// @match         *://*.castle.wonderhill.com/platforms/facebook/game
// @include       *://plus.google.com/games/659749063556*
// @include       *://plus.google.com/*/games/659749063556*
// @include       *://*.googleusercontent.com/gadgets/ifr?url=app://659749063556*
// @match         *://plus.google.com/games/659749063556*
// @match         *://*.googleusercontent.com/gadgets/ifr?url=app://659749063556*
// @version       20111020a
// @icon          http://www.mmogwiki.com/scripts/dragonsofatlantis/powertools/logo.png
// ==/UserScript==

/********************************************************************************
 * INFORMATION                                                                  *
 *                                                                              *
 * Name: DoA Power Tools II                                                     *
 * Version: 20111020a                                                           *
 * Last Modified: Saturday, 20 October 2011 20:00  GMT+3                        *
 * Original Authors: G.Jetson, Runey & Wham                                     *
 * Current  Authors: Runey & La Larva                                           *
 *                                                                              *
 * ACKNOWLEDGEMENTS                                                             *
 *                                                                              *
 * DoA Power Tools II has been written from the ground up and is not            *
 * considered a fork of any other project. However it could never of happened   *
 * without the work done by many scriptwriters on the original DoA Power Tools  *
 * and its many mods.                                                           *
 *                                                                              *
 * DoA Power Tools by George Jetson                                             *
 *  - <http://userscripts.org/scripts/show/102481>                              *
 * DoA Power Tools Plus by Runey                                                *
 *  - <http://userscripts.org/scripts/show/104301>                              *
 * DoA Power Tools Mod by Wham                                                  *
 *  - <http://userscripts.org/scripts/show/103833>                              *
 * DoA Power Tools Plus II by La Larva                                          *
 *  - <http://userscripts.org/scripts/show/114012>                              *
 *                                                                              *
 * DEVELOPMENT                                                                  *
 *                                                                              *
 * If you wish to contribute to the development of DoA Power Tools II you       *
 * can do so at <INSERT WIKI ADDRESS HERE WHEN DONE>.                           *
 *                                                                              *
 * If you wish to contribute to the development of DoA Power Tools II you       *
 * can do so at <INSERT WIKI ADDRESS HERE WHEN DONE>.                           *
 *                                                                              *
 * If you wish to fork this project then you may do so as long as the following *
 * conditions are met.                                                          *
 *  - The GNU General Public License version 3 or later is used                 *
 *  - All acknowledgements MUST be included in the source code                  *
 *  - A link to the API at MMOG Wiki MUST be included in the source code        *
 *  - It MUST be free (though as per the GNU Public License a small fee for     *
 *    distribution and/or support may be charged)                               *
 *                                                                              *
 * LICENSE                                                                      *
 *                                                                              *
 * Released under the GPL license                                               *
 * http://www.gnu.org/copyleft/gpl.html                                         *
 *                                                                              *
 * This program is free software: you can redistribute it and/or modify it      *
 * under the terms of the GNU General Public License as published by the        *
 * Free Software Foundation, either version 3 of the License, or                *
 * (at your option) any later version.                                          *
 *                                                                              *
 * This program is distributed in the hope that it will be useful, but          *
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY   *
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License     *
 * for more details.                                                            *
 *                                                                              *
 * You should have received a copy of the GNU General Public License along with *
 * this program.  If not, see <http://www.gnu.org/licenses/>.                   *
 ********************************************************************************/
(function() {
// jQuery Alias
var $J; 

// Check if we are in the right sites (if the Metadata Blocks dont work)
/*
if ( !( (/apps\.facebook\.com\/dragonsofatlantis/.test(document.location.href) && /rubies/.test(document.location.pathname) == false) ||
  /castle\.wonderhill\.com\/platforms\/facebook\/game/.test(document.location.href) ||
  /plus\.google\.com\/games\/659749063556/.test(document.location.href) || 
  /googleusercontent\.com\/gadgets\/ifr\?url\=app\:\/\/659749063556/.test(document.location.href)
 )){
  return;
*/

// Script Version: Year, Month, Day, Revision, Maturity (e.g. YYYMMDDa_BETA)
var SCRIPT_VERSION = '20111020a'; 

// DoA API Version
var API_VERSION  = 19;

// LocalStorage Data Version (Change only if made any changes in the structure of the Data Object)
var DATA_VERSION = '20111020a'; 

"use strict";

/********************************************************************************
 * Load a JavaScript library                                                    *
 *                                                                              *
 * Current actions:                                                             *
 *  - Loads jQuery 1.6.4                                                        *
 *                                                                              *
 * Notes:                                                                       *
 *  - Firefox uses @require in the Metadata Block                               *
 *  - Safari includes the library as part of the extension                      *
 ********************************************************************************/
 function addLibrary(callback) {
    var script = document.createElement("script");
    script.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js");
    script.addEventListener('load', function() {
        var script = document.createElement("script");
        script.textContent = "(" + callback.toString() + ")();";
        document.body.appendChild(script);
    }, false);
    document.body.appendChild(script);
}


/********************************************************************************
 * Check to see if script is running in an iframe or not and removes            *
 * unnecessary elements before continuing.                                      *
 *                                                                              *
 * Current actions:                                                             *
 *  - Set width all parent div of iframe to 100%                                *
 *  - Hide unwanted div in window.top                                           *
 *  - Hide unwanted div in iframe                                               *
 *  - Set width of #content div to 760px                                        *
 *                                                                              *
 * To avoid conflict with other libraries, that may be running on the same      *
 * page, the default alias of $ is changed to $J.                                *
 ********************************************************************************/
function preparePage() {
 $J = jQuery.noConflict();
    var iframe,
        object = "#castlemania_swf",
        platform;

    if (window.location.href.indexOf("facebook") != -1) {
        iframe = "#iframe_canvas";
        platform = "facebook";
    } else if (window.location.href.indexOf("google") != -1) {
        iframe = "#oz-gadgets-canvas-iframe-659749063556";
        platform = "google";
    }
 
    if (window.top == window.self) {
        function setWide() {    
            clearTimeout;
            if ($J(iframe).length < 1) {
                setTimeout(setWide, 100);
                return;
            }
            switch (platform) {
                case "facebook" :
                    $J("#rightCol").css("display", "none");
         $J("#blueBar").css("position", "relative");
                break;
    case "google" :
     alert('Google');
                    $J(".Pca").css("display", "none");
                break;
            }
            $J(iframe).parents().width("100%");
        }
        setWide();
    } else {
        function setHigh() {    
            clearTimeout;
            if ($J(object).length < 1) {
                setTimeout(setHigh, 100);
                return;
            }
            switch (platform) {
                case "facebook" :
                    $J("#hd > div").css("display", "none");
        $J("#ft").css("display", "none");
                    $J("#cn").parent().append($J("#hd"));
                break;
                case "google" :
                    $J("#pane_hd").css("display", "none");
                break;
            }
            $J("#container").width("760px");

           initScript(object);
        }
        setHigh();
    }
}



/********************************************************************************
 * Setup global variables that can be used anywhere within the script           *
 *                                                                              *
 * NAMING CONVENTIONS (http://javascript.crockford.com/code.html)               *
 *  - variables and functions should begin with a lowercase letter              *
 *  - constructor functions should begin with a capital letter                  *
 *  - global variable should be all capitals                                    *
 ********************************************************************************/
 
// Constructor function 
var  AutoCollect,
  Buildings,
  Data, 
  DefaultDataOptions,
  $mainBox, 
  Manifest, 
  Map,
  Marches,
  Messages,
  MyAjax, 
  RequestQueue,
  ScriptStyles,
  Seed,
  $startUpBox,
  Tabs = {},
  ToTranslate = {},
  Translation,
  UID = {},
  UIDN = {},
  VerboseLog;
 
 // Global Functions
var translate = verboseLog = actionLog = function(){};

var LANG_OBJECT;
 
 
// Unique Identifier
UID = {};
UIDN = {};

function makeUID(len){
 var len = ( len != undefined ? len : 20);
 var chars = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','u','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9','_'];
 var uid = chars[Math.floor(Math.random()*54)];
 for(var i = 0; i < len; i++)
 {
  uid += chars[Math.floor(Math.random()*64)];
 }
 return uid;
}

function getUID(name){
 return UID[name] != undefined ? UID[name] : name;
}

function setUID(name){
 var uid = makeUID();
 while(UIDN[uid] != undefined){
  uid = makeUID();
 }
 UIDN[uid] = 1;
 UID[name] = uid;
 return uid;
}
 
 


 
 
 
 
// init PowerTools

function initScript (SWF_OBJECT) {

/********************************************************************************
* All global variables MUST be set here or they will not be available to all   *
* functions throughout the script.                                             *
********************************************************************************/

var SCRIPT_NAME   = 'DoA Power Tools Plus II';
var SCRIPT_URL_ERROR = 'http://www.mmogwiki.com/forum/index.php?f=5&t=409&rb_v=viewtopic';
var SCRIPT_TITLE  = '';


// Tab order
var INFO_TAB_ORDER  = 1;     
var WAVE_TAB_ORDER  = 2;
var ATTACK_TAB_ORDER = 3;
var JOBS_TAB_ORDER  = 4;
var LOG_TAB_ORDER  = 5;
var OPTIONS_TAB_ORDER = 6;
var DEBUG_TAB_ORDER  = 99;

// Tab enable/disable
var INFO_TAB_ENABLE  = true;     
var WAVE_TAB_ENABLE  = true;
var ATTACK_TAB_ENABLE = true;
var JOBS_TAB_ENABLE  = true;
var LOG_TAB_ENABLE  = true;
var OPTIONS_TAB_ENABLE = true;
var DEBUG_TAB_ENABLE = false;

// CHECK THESE VARIABLES
var SCRIPT_STARTUP_DELAY= Math.randRange(7000,15000);
var DEBUG_TRACE_AJAX = 2;
var DEBUG_MARCHES  = false;
var ATTACK_MIN_DELAY = 30; // WARNING: Reducing this value can cause account banned by Kabam
var EMULATE_NET_ERROR = 0;  // percentage
var ENABLE_WINLOG  = false;
var ALERT_ON_BAD_DATA = false;


var BUTTON_BGCOLOR  = '#436';
var JOB_BUTTON_BGCOLOR = '#049C93';
var TIMER_COLOR   = '#2B4988';

var LANG_CODE = navigator.language.substring(0,2).toLowerCase();
var IS_NOT_NATIVE_LANG = (LANG_CODE != 'en');

var IS_CHROME = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;


var TITLE_WORDS = ['aerialcombat', 'alliance', 'ancestralseal', 'aquatroop', 'armor', 'army', 'arsenal', 'blast', 'blitz', 'bog', 'bolt', 'bore', 'chest', 'complete', 'crimsonbull', 'dragonhearts', 'darkwarpdevice', 'divinelight', 'divinerations', 'field', 'firedragon', 'firetroop', 'fog', 'forest', 'fortress', 'giant', 'gold', 'greatdragon', 'hop', 'lake', 'levitation', 'life', 'lumbermill', 'metalsmith', 'might', 'military', 'mining', 'minotaur', 'mountain', 'ore', 'outposts', 'plain', 'purplebones', 'quarry', 'rookery', 'rubies', 'silo', 'skip', 'source', 'speed', 'spy', 'stone', 'technology', 'wall', 'warp', 'wildernesses', 'windtroop', 'wood', 'world'];


//
// Variables strings
//
// Terrain
var kAnthropusCamp  = 'AnthropusCamp';
var kCity    = 'City';
var kForest    = 'Forest';
var kGrassland   = 'Grassland';
var kHill    = 'Hill';
var kLake    = 'Lake';
var kMountain   = 'Mountain';
var kOutpost   = 'Outpost';
var kPlain    = 'Plain';
var kBog    = 'Bog';
var kWildernesses  = 'Wildernesses';

// Buildings
var kDragonKeep   = 'DragonKeep';
var kFactory   = 'Factory';
var kFarm    = 'Farm';
var kFortress   = 'Fortress';
var kGarrison   = 'Garrison';
var kHome    = 'Home';
var kLumbermill   = 'Lumbermill';
var kMetalsmith   = 'Metalsmith';
var kMine    = 'Mine';
var kMusterPoint  = 'MusterPoint';
var kOfficerQuarter  = 'OfficerQuarter';
var kQuarry    = 'Quarry';
var kRookery   = 'Rookery';
var kScienceCenter  = 'ScienceCenter';
var kSentinel   = 'Sentinel';
var kSilo    = 'Silo';
var kStorageVault  = 'StorageVault';
var kTheater   = 'Theater';
var kTrainingCamp  = 'TrainingCamp';
var kWall    = 'Wall';

// Research
var kAgriculture  = 'Agriculture';
var kWoodcraft   = 'Woodcraft';
var kMasonry   = 'Masonry';
var kMining    = 'Mining';
var kClairvoyance  = 'Clairvoyance';
var kRapidDeployment = 'RapidDeployment';
var kBallistics   = 'Ballistics';
var kMetallurgy   = 'Metallurgy';
var kMedicine   = 'Medicine';
var kDragonry   = 'Dragonry';
var kLevitation   = 'Levitation';
var kMercantilism  = 'Mercantilism';
var kAerialCombat  = 'AerialCombat';

// Troops
var kArmoredTransport = 'ArmoredTransport';
var kBattleDragon  = 'BattleDragon';
var kConscript   = 'Conscript';
var kFireMirror   = 'FireMirror';
var kGiant    = 'Giant';
var kHalberdsman  = 'Halberdsman';
var kLongbowman   = 'Longbowman';
var kMinotaur   = 'Minotaur';
var kPorter    = 'Porter';
var kSpy    = 'Spy';
var kSwiftStrikeDragon = 'SwiftStrikeDragon';

// Special Troops
var kAquaTroop   = 'AquaTroop';
var kFireTroop   = 'FireTroop';
var kStoneTroop   = 'StoneTroop';
var kWindTroop   = 'WindTroop';

// Dragons
var kFireDragon   = 'FireDragon';
var kGreatDragon  = 'GreatDragon';
var kStoneDragon  = 'StoneDragon';
var kWaterDragon  = 'WaterDragon';
var kWindDragon   = 'WindDragon';

var kPackDragon   = 'PackDragon';

// Items
var kAquaTroopRespirator = 'AquaTroopRespirator';
var kStoneTroopItem   = 'StoneTroopItem';
var kFireTroopItem   = 'FireTroopItem';
var kWindTroopItem   = 'WindTroopItem';

var kGDBodyArmor   = 'GreatDragonBodyArmor';
var kGDClawGuards   = 'GreatDragonClawGuards';
var kGDHelmet    = 'GreatDragonHelmet';
var kGDTailGuard   = 'GreatDragonTailGuard';

// Error messages
var kFatalSeedTitle = 'ERROR WHILST FETCHING DATA FROM SERVER';
var kFatalSeedMsg = 'Please disable the script and see if you are able to play the game manually. If normal play is possible then enable the script and try again. If the error persists please read the following post before submitting a report. If normal play is not possible then wait until it is and try again.';
var kFatalSWF  = '<B>Error initializing:</b><BR><BR>Unable to find SWF element';
var kStartupErr  = 'Unable to start $SCRIPT_NAME$ <BR>';
var kInitErr  = '<B>Error initializing:</b><BR><BR>';







/*******************************************************************************
***************************      TRANSLATIONS      ****************************
*******************************************************************************/
switch ( LANG_CODE ){
 /*******************************************************************************
  German ( by Native )
 *******************************************************************************/
case 'de':
 LANG_OBJECT = {
 'above the first value':'über dem ersten Wert',
 'Action Logs':'Aktion Logs',
 'Actions':'Aktionen',
 'and':'und',
 'Are you sure you want to':'Sind Sie sicher, dass Sie',
 'at':'bei',
 'Attack One Target in Waves':'Wellenangriff auf ein Ziel',
 'Attack sent to':'Angriffe an',
 'Attacking':'Angriff',
 'Attacks Configuration':'Angriff Konfiguration',
 'Attacks Logs':'Angriffs-Logbücher',
 'Attacks stopped momentarily to prevent server blocking':'Angriffe gestoppt momentan auf Server blockiert verhindern',
 'Attacks':'Attacken',
 'Auto Refresh every':'Auto Auffrischen jeder',
 'Automatically':'Automatisch',
 'Awaiting task completion notification':'Erwarte Abschlussbenachrichtingung der Aufgabe',
 'Building':'Gebäude',
 'Busy':'Beschäftigt',
 'by':'durch',
 'Clear last attack on all maps':'Lösche letzten Angriff auf alle Karten',
 'Clear last attack on current map':'Lösche letzten Angriff auf aktueller Karte',
 'Config':'Konfiguration',
 'Console Logs':'Konsolen Logbücher',
 'Console':'Konsole',
 'Coordinates':'Koordinaten',
 'Days':'Tage',
 'Delay Between Attacks':'Verzögerung zwischen Angriffen',
 'Disabled':'Deaktiviert',
 'Distance must be between':'Entfernung muss zwischen',
 'Distance':'Entfernung',
 'Enable':'Aktivieren',
 'Enabled':'Aktiviert',
 'Error':'Fehler',
 'First value must be between':'Erster Wert muss zwischen',
 'Game Options':'Spiel Optionen',
 'Going to the coords':'Gehen um die Koordinaten',
 'Hiding':'Verstecken',
 'Hour':'Stunde',
 'Hours':'Stunden',
 'Info':'Info',
 'Invalid Date From':'Ungültiges Datum aus',
 'Invalid Date To':'Ungültiges Datum zu',
 'Invalid delays':'Ungültige Verzögerungen',
 'Invalid number of troops':'Ungültige Anzahl von Truppen',
 'Invalid Range Date':'Ungültiger Bereichs Datum',
 'Last Attack':'Letzter Angriff',
 'Loaded':'Geladen',
 'Logs':'Logbücher',
 'Manual attack sent to':'Manueller Angriff gesendet an',
 'Maximum simultaneous marches':'Maximale gleichzeitige Märsche',
 'miles':'Meilen',
 'Minimum Housing':'Minimale Bürger',
 'Minimum Resource Levels':'Minimale Ressourcen',
 'Minutes':'Minuten',
 'Full':'ausgelastet',
 'No Generals Available':'Keine Generäle verfügbar',
 'No targets or troops available':'Keine Ziele oder Truppen verfügbar',
 'No troops available':'Keine Truppen verfügbar',
 'No Troops Defined':'Keine Truppen ausgewählt',
 'Not enough':'Nicht genügend',
 'Not':'Nicht',
 'of inactivity':'der Inaktivität',
 'of':'der',
 'Opening the map on the last position':'Das Öffnen der Karte auf der letzten position',
 'Options':'Optionen',
 'Outpost 1':'Wasser-Außenposten',
 'Outpost 2':'Stein-Außenposten',
 'Outpost 3':'Feuer-Außenposten',
 'Outpost 4':'Wind-Außenposten',
 'Permanent Data':'Dauerhaft Daten',
 'Preparing Attack':'Vorbereitung Angriff',
 'Refresh':'Aktualisieren',
 'Researching':'Forschen',
 'Retry in':'Wiederholen in',
 'Run Time':'Laufzeit',
 'Safe Mode':'Sicherer Modus',
 'Scanning Map':'Scanne Karte innerhalb von $NUM$ Meilen <BR> Ungefähre restliche Wartezeit',
 'Script Options':'Skript-Optionen',
 'Search Radius':'Suchradius',
 'Second value must be at least':'Zweiter Wert muss mindestens',
 'Seconds':'Sekunden',
 'Send Dragon every certain number of waves':'Send Drachen jeder bestimmte Anzahl von Wellen',
 'Start Date':'Startdatum',
 'Stop if any troops lost':'Stopp bei Truppenverlust',
 'Successfully':'Erfolgreich',
 'Summary':'Übersicht',
 'Targets':'Ziele',
 'Task Completed':'Erledigt',
 'Tasks':'Aufgaben',
 'Too many errors,  disabling auto train':'Zu viele Fehler, deaktivere automatische Ausbildung',
 'Too many troops for muster point level':'Maximale Truppenanzahl laut Truppensammelplatz-Level überschritten',
 'Training Configuration':'Ausbildungs Konfiguration',
 'Training queue':'Trainings-Warteschlange',
 'Troops for Wave Attack':'Truppen für Wellenangriff',
 'Troops lost':'Truppen verloren',
 'Troops Not Defined':'Truppen nicht definiert',
 'Use the Levels Tab to select attack areas':'Benutze die Level-Tabelle um Angriffsbereiche auszuwählen',
 'Userset maximum marches reached':'Eingestellte maximale Märsche erreicht',
 'Verbose logging':'Ausführlichen Logbücher',
 'waiting':'warten',
 'Warnings':'Warnung',
 'Wave attack to':'Wellenangriff zu',
 'Wave':'Welle',
 'Window drag':'Fensterverschiebung',
 '~ArmoredTransport':'Luftis',       /* Abbreviation (max. 8 characters) */  
 '~BattleDragon':'KampfDr',   /* idem as above */  
 '~Conscript':'Rekrut',    /* idem above */  
 '~FireDragon':'FeuerDra',   /* idem */  
 '~FireMirror':'Feuersp',   /* idem */  
 '~FireTroop':'Pyros',    /* idem */  
 '~Giant':'Riesen',     /* idem */  
 '~GreatDragon':'GrossDr',   /* idem */  
 '~Halberdsman':'Hellebar',   /* idem */  
 '~Longbowman':'Bogi',    /* idem */  
 '~Minotaur':'Mino',     /* idem */ 
 '~PackDragon':'PckDrg',    /* idem */ 
 '~Porter':'Träger',     /* idem */  
 '~Spy':'Spion',      /* idem */  
 '~StoneDragon':'SteinDr',   /* idem */  
 '~StoneTroop':'Oger',    /* idem */  
 '~SwiftStrikeDragon':'kFD',   /* idem */  
 '~WaterDragon':'WasserDr',   /* idem */  
 '~WindDragon':'WindDr',    /* idem */  
 '~WindTroop':'Banshee',    /* idem */
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Español (by La Larva)
 *******************************************************************************/
case 'es':
 LANG_OBJECT = {
 'above the first value':'por encima del primer valor',
 'Action Log':'Reporte de Acciones',
 'Actions':'Acciones',
 'and':'y',
 'Are you sure you want to':'Esta seguro que desea',
 'at':'en',
 'Attack One Target in Waves':'Ataques en Oleadas a Objetivos',
 'Attack sent to':'Ataque enviado a',
 'Attacking':'Atacando',
 'Attacks Configuration':'Configuración de Ataques',
 'Attacks Logs':'Registro de Ataques',
 'Attacks stopped momentarily to prevent server blocking':'Los ataques se detuvieron momentáneamente para evitar el bloqueo del servidor',
 'Attacks':'Ataques',
 'Auto Refresh every':'Auto Recargar la página cada',
 'Automatically':'Automáticamente',
 'Awaiting task completion notification':'En espera de la notificación de finalización de la tarea',
 'Building':'Edificando',
 'Busy':'Ocupado',
 'by':'por',
 'Clear last attack on all maps':'Borrar todos los registros de últimos ataques',
 'Clear last attack on current map':'Borrar registro de últimos ataques actuales',
 'Config':'Configuración',
 'Console Log':'Registros de Consola',
 'Console':'Consola',
 'Coords':'Coords',
 'd':'d',
 'Days':'Día(s)',
 'Delay Between Attacks':'Tiempo de retraso entre ataques',
 'Disabled':'Desactivado',
 'Distance must be between':'La distancia debe estar entre',
 'Distance':'Distancia',
 'Enable':'Activar',
 'Enabled':'Activado',
 'Error':'Error',
 'First value must be between':'El primer valor debe ser de',
 'Game Options':'Opciones del Juego',
 'Going to the coords':'Llendo a las Coordenadas',
 'h':'h',
 'Hiding':'Esconder Tropas',
 'Hour':'Hora',
 'Hours':'Hora(s)',
 'Info':'Info',
 'Invalid Date From':'Formato de Fecha de Inicio Invalido',
 'Invalid Date To':'Formato de Fecha de Finalizacion Invalido',
 'Invalid delays':'Intervalo de Retraso Invalido',
 'Invalid number of troops':'Numero invalido de tropas',
 'Invalid Range Date':'Rango de Fecha Invalido',
 'Last Attack':'Último Ataque',
 'Loaded':'Cargado',
 'Logs':'Registros',
 'm':'m',
 'Manual attack sent to':'Ataque Manual enviado a',
 'Maximum simultaneous marches':'Máximo de Marchas Simultáneas',
 'miles':'millas',
 'Minimum Housing':'por Mínimo de Casas',
 'Minimum Resource Levels':'por Mínimo de Niveles de Recursos',
 'Minutes':'Minuto(s)',
 'Full':'Lleno',
 'No Generals Available':'No hay generales disponibles',
 'No targets or troops available':'Sin objetivos o tropas disponibles',
 'No troops available':'No hay suficientes tropas',
 'No Troops Defined':'No Hay Tropas Definidas',
 'Not enough':'No hay suficiente',
 'Not':'No',
 'of inactivity':'de inactividad',
 'of':'de',
 'Opening the map on the last position':'Abriendo el mapa en la última posición',
 'Options':'Opciones',
 'Outpost 1':'Ciudad del Agua',
 'Outpost 2':'Ciudad de la Piedra',
 'Outpost 3':'Ciudad del Fuego',
 'Outpost 4':'Ciudad del Viento',
 'Permanent Data':'Datos Permanentes',
 'Preparing Attack':'Preparando el Ataque',
 'Refresh':'Actualizar',
 'Researching':'Investigando',
 'Retry in':'Reintentando en',
 'Run Time':'Tiempo de Ejecucción',
 's':'s',
 'Safe Mode':'Modo Seguro',
 'Scanning Map':'Buscando datos en $NUM$ millas a la redonda<BR>Este proceso puede demorar un tiempo',
 'Script Options':'Opciones del Script',
 'Search Radius':'Radio de Busqueda',
 'Second value must be at least':'El segundo valor debe ser por lo menos de',
 'Seconds':'Segundo(s)',
 'Send Dragon every certain number of waves':'Enviar Dragón cada cierto número de oleadas',
 'Start Date':'Fecha de Inicio',
 'Stop if any troops lost':'Detener ataques si se pierden tropas',
 'Successfully':'Exitosamente',
 'Summary':'Detalles',
 'Targets':'Objetivos',
 'Task Completed':'Tarea Finalizada',
 'Tasks':'Tareas',
 'Too many errors, disabling auto training':'Demasiados errores, Desactivado Adiestramientos',
 'Too many troops for muster point level':'Demasiadas tropas para el Nivel actual del Punto de Encuentro',
 'Training Configuration':'Configuración de Adiestramientos',
 'Training queue':'Encolando Adistramientos',
 'Troops for Wave Attack':'Tropas para Ataques Masivos',
 'Troops lost':'¡Se han perdido tropas',
 'Troops Not Defined':'No Hay Tropas Definidas',
 'Use the Levels Tab to select attack areas':'Usar la solapa de Niveles para seleccionar el rango de ataque',
 'Userset maximum marches reached':'Llegaste al limite defindo por ti de marchas',    
 'Verbose logging':'Registro detallado',
 'waiting':'esperando',
 'Warnings':'Advertencias',
 'Wave attack to':'Ataque en Oleada a',
 'Wave':'Oleadas',
 'Window drag':'Arrastrar la ventana',
 '~AquaTroop':'Tritón',   /* Abbreviation (max. 8 characters) */
 '~ArmoredTransport':'TransB', /* Abbreviation (max. 8 characters) */
 '~BattleDragon':'DrgComb',  /* idem as above */
 '~Conscript':'Reclu',   /* idem above */
 '~FireDragon':'Drg Fueg',  /* idem */
 '~FireMirror':'Espejo',   /* idem */
 '~FireTroop':'Magma',   /* idem */
 '~Giant':'Gigante',    /* idem */
 '~GreatDragon':'Gran Drg',  /* idem */
 '~Halberdsman':'Alabar',  /* idem */
 '~Longbowman':'Arq',   /* idem */
 '~Minotaur':'Mino',    /* idem */
 '~PackDragon':'Drg Carg',  /* idem */ 
 '~Porter':'Porteador',   /* idem */
 '~Spy':'Espía',     /* idem */
 '~StoneDragon':'Drg Pét',  /* idem */
 '~StoneTroop':'Ogro',   /* idem */
 '~SwiftStrikeDragon':'DrgARap', /* idem */
 '~WaterDragon':'Drg Agua',  /* idem */
 '~WindDragon':'Drg Viet',  /* idem */
 '~WindTroop':'Bansh',   /* idem */
 '~Zzz':'Zzz'
 };
 break;
 /**********************************************************************
      Français  (by Randalph)
 ***********************************************************************/
case 'fr':
 LANG_OBJECT = {
 'above the first value':'supérieure à la premiere',
 'Action Logs':'Journal d\'évenements',
 'Actions':'Actions',
 'and':'et',
 'Are you sure you want to':'Etes-vous sûr que vous voulez',
 'at':'à',
 'Attack One Target in Waves':'Attaquer une cible par vagues',
 'Attack sent to':'Actions en  cours',
 'Attacking':'Attaque',
 'Attacks Configuration':'Configuration',
 'Attacks Logs':'Détails des attaques',
 'Attacks stopped momentarily to prevent server blocking':'Attaques stoppée momentanément pour éviter le blocage du serveur',
 'Attacks':'Attaques',
 'Auto Refresh every':'Actualisation automatique toutes les',
 'Automatically':'Automatique',
 'Awaiting task completion notification':'En attente de la notification de fin des tâches',
 'Building':'Bâtiment',
 'Busy':'Occupé',
 'by':'par',
 'Clear last attack on all maps':'Réinitialiser toutes les cartes',
 'Clear last attack on current map':'Réinitialiser les attaques sur la carte',
 'Config':'Config',
 'Console Logs':'Console Logs',
 'Console':'Console',
 'Coordinates':'Coordonnées',
 'Days':'Jours',
 'Delay Between Attacks':'Délai entre les attaques',
 'Disabled':'Désactiver',
 'Distance must be between':'La distance doit être comprise entre',
 'Distance':'Distance',
 'Enable':'Activer',
 'Enabled':'Activé',
 'Error':'Erreur',
 'First value must be between':'La valeur du délai doit être comprise entre',
 'Game Options':'Options de jeu',
 'Going to the coords':'Aller aux coordonnées',
 'Hiding':'Cacher',
 'Hour':'Heure',
 'Hours':'Heures',
 'Info':'Info',
 'Invalid Date From':'Date non valide de',
 'Invalid Date To':'Date non valide pour',
 'Invalid delays':'Délai invalide',
 'Invalid number of troops':'Nombre d\'unités invalide',
 'Invalid Range Date':'Format de la date incorrect',
 'Last Attack':'Dernière attaque',
 'Loaded':'Script chargé',
 'Logs':'Journal',
 'Manual attack sent to':'Attaque manuelle vers',
 'Maximum simultaneous marches':'Maximum de marches simultanées',
 'miles':'miles',
 'Minimum Housing':' Population Minimum',
 'Minimum Resource Levels':' Niveaux de Ressources Minimum',
 'Minutes':'Minutes',
 'Full':'Complet',
 'No Generals Available':'Pas de généraux libre',
 'No targets or troops available':'Aucune cibles ou troupes disponibles',
 'No troops available':'Pas de troupes disponibles',
 'No Troops Defined':'Pas de troupes définies',
 'Not enough':'Pas assez',
 'Not':'Non',
 'of inactivity':'d\'inactivité',
 'of':'sur',
 'Opening the map on the last position':'Ouvrir la carte à la dernière position',
 'Options':'Options',
 'Outpost 1':'Dragon aquatique',
 'Outpost 2':'Dragon de pierre',
 'Outpost 3':'Dragon de feu',
 'Outpost 4':'Dragon éolien',
 'Permanent Data':'Données Permanente',
 'Preparing Attack':'Préparer l\'attaque',
 'Refresh':'Actualiser',
 'Researching':'Recherche en cours',
 'Retry in':'nouvel essai dans',
 'Run Time':'Temps d\'exécution',
 'Safe Mode':'Mode Sans échec',
 'Scanning Map':'Balayage de la carte sur $NUM$ miles <BR> Attendez la fin du scan, ne quittez pas la page',
 'Script Options':'Options de script',
 'Search Radius':'Rayon de balayage',
 'Second value must be at least':'La deuxième valeur doit être au moins',
 'Seconds':'Secondes',
 'Send Dragon every certain number of waves':'Regler l\’ordre de marche des Dragons élémentaux',
 'Start Date':'Date de début',
 'Stop if any troops lost':'Désactiver en cas de pertes',
 'Successfully':'Réussi',
 'Summary':'Général',
 'Targets':'Cibles',
 'Task Completed':'Tache terminée',
 'Tasks':'Taches',
 'Too many errors,  disabling auto train':'Trop d\'erreurs, entrainement automatique désactivé',
 'Too many troops for muster point level':'Déploiement maximal atteint',
 'Training Configuration':'Configuration',
 'Training queue':'File de formation en attente',
 'Troops for Wave Attack':'Sélectionnez vos troupes',
 'Troops lost':'Troupes perdues',
 'Troops Not Defined':'Aucunes troupes définies',
 'Use the Levels Tab to select attack areas':'Utilisez l\'onglet "Niveaux" et sélectionnez la cible',
 'Userset maximum marches reached':'Maximum de marches simultanés atteinte',
 'Verbose logging':'Journal d\'évenements',
 'waiting':'en attente',
 'Warnings':'Avertissements',
 'Wave attack to':'Attaque en vagues vers',
 'Wave':'Vagues',
 'Window drag':'Glisser/déposer',
 '~AquaTroop':'Fang',   /* Abbreviation (max. 8 characters) */
 '~ArmoredTransport':'Ballons', /* Abbreviation (max. 8 characters) */
 '~BattleDragon':'Drg Gr',  /* idem as above */
 '~Conscript':'Conscrit',  /* idem above */
 '~FireDragon':'Drg Feu',  /* idem */
 '~FireMirror':'Miroir',   /* idem */
 '~FireTroop':'Magma',   /* idem */
 '~Giant':'Géant',    /* idem */
 '~GreatDragon':'Grd Drg',  /* idem */
 '~Halberdsman':'Halbrd',  /* idem */
 '~Longbowman':'Archer',   /* idem */
 '~Minotaur':'Mino',    /* idem */
 '~PackDragon':'PckDrg',   /* idem */ 
 '~Porter':'Porteur',   /* idem */
 '~Spy':'Espion',    /* idem */
 '~StoneDragon':'DrgPierre',  /* idem */
 '~StoneTroop':'Ogre',   /* idem */
 '~SwiftStrikeDragon':'Rap Drg', /* idem */
 '~WaterDragon':'Drg Aqua',  /* idem */
 '~WindDragon':'Drg Vent',  /* idem */
 '~WindTroop':'Banshee',   /* idem */
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Italiano (by Boaro)
 *******************************************************************************/
case 'it':
 LANG_OBJECT = {
 'above the first value':'al di sopra del primo valore',
 'Action Logs': 'Log azione',
 'Actions': 'Azioni',
 'and': 'e',
 'Are you sure you want to':'Sei sicuro che vuoi',
 'at': 'a',
 'Attack One Target in Waves': 'Attacco un obiettivo in Onde',
 'Attack sent to': 'Attacco inviato a',
 'Attacking': 'Attaccare',
 'Attacks Configuration': 'Attacchi di configurazione',
 'Attacks Logs': 'Registro attacchi nel log',
 'Attacks stopped momentarily to prevent server blocking':'Attacchi fermati momentaneamente per evitare il blocco del server',
 'Attacks': 'Attacchi',
 'Auto Refresh every':'Auto Aggiorna ogni',
 'Automatically': 'Automatico',
 'Awaiting task completion notification': 'In attesa di notifica completamento delle attività',
 'Building': 'Costruzione',
 'Busy': 'Occupato',
 'by': 'da',
 'Clear last attack on all maps': 'Cancella ultimo attacco su tutte le mappe',
 'Clear last attack on current map': 'Cancella ultimo attacco sulla mappa corrente',
 'Config': 'Configurazione',
 'Console Logs': 'Console LOG',
 'Console': 'Console',
 'Coordinates': 'Coordinate',
 'Days': 'Giorni',
 'Delay Between Attacks': 'Intervallo tra gli attacchi',
 'Disabled': 'Disabilita',
 'Distance must be between': 'La distanza deve essere tra',
 'Distance': 'Distanza',
 'Enable': 'Abilita',
 'Enabled': 'Abilitato',
 'Error': 'Errore',
 'First value must be between': 'Il primo valore deve essere compreso tra',
 'Game Options': 'Opzioni di gioco',
 'Going to the coords':'Andando alle coordinate',
 'Hiding': 'Truppe NASCOSTE',
 'Hour': 'Ora',
 'Hours': 'Ore',
 'Info': 'Info',
 'Invalid Date From': 'Data Invalida Da',
 'Invalid Date To': 'Data Invalida A',
 'Invalid delays': 'Ritardo non valido',
 'Invalid number of troops': 'Numero di truppe non Valido',
 'Invalid Range Date': 'Non valido Data Range',
 'Last Attack': 'Ultimo attacco',
 'Loaded': 'Caricato',
 'Logs': 'Registri',
 'Manual attack sent to': 'Attacco manuale inviato',
 'Maximum simultaneous marches': 'Massime Marce simultanee',
 'miles':'miglia',
 'Minimum Housing': 'Usa come minimo',
 'Minimum Resource Levels': 'Livelli minimi di risorse',
 'Minutes': 'Minutei',
 'Full': 'Completo',
 'No Generals Available': 'Nessun generale disponibile',
 'No targets or troops available': 'Nessun obiettivo o truppa disponibile',
 'No troops available': 'Nessuna truppa disponibile',
 'No Troops Defined': 'Nessuna truppa definita',
 'Not enough': 'Non hai abbastanza',
 'Not': 'Non',
 'of inactivity':'di inattività',
 'of': 'di',
 'Opening the map on the last position':'Aprendo la mappa sul ultima posizione',
 'Options': 'Opzioni',
 'Outpost 1': 'Città d\'Acqua',
 'Outpost 2': 'Città di Pietra',
 'Outpost 3': 'Città di Fuoco',
 'Outpost 4': 'Città di vento',
 'Permanent Data':'Dati Permanenti',
 'Preparing Attack':'Preparazione attacco',
 'Refresh': 'Aggiorna',
 'Researching': 'La ricerca',
 'Retry in':'Riprova a',
 'Run Time': 'Run Time',
 'Safe Mode': 'Modalità provvisoria',
 'Scanning Map': 'La scansione mappa entro $NUM$ miglia <BR> Questo dovrebbe richiedere circa a tempo',
 'Script Options': 'Opzioni Script',
 'Search Radius':'Raggio di ricerca',
 'Second value must be at least':'Secondo valore deve essere almeno',
 'Seconds': 'Secondi',
 'Send Dragon every certain number of waves':'Inviare Drago ogni certo numero di Onda',
 'Start Date': 'Data Start',
 'Stop if any troops lost': 'Stop eventuali truppe perse',
 'Successfully': 'Successo',
 'Summary': 'Sintesi',
 'Targets': 'Obiettivi',
 'Task Completed': 'Attività Completato',
 'Tasks': 'Attività',
 'Too many errors,  disabling auto train': 'Troppi errori, treno auto invalidante',
 'Too many troops for muster point level': 'Le truppe Troppi per il livello di radunare punto',
 'Training Configuration': 'Configurazione di Formazione',
 'Training queue': 'Coda di Formazione',
 'Troops for Wave Attack': 'Le truppe di attacco Onda',
 'Troops lost': 'Truppe perso',
 'Troops Not Defined': 'Truppe Non definito',
 'Use the Levels Tab to select attack areas': 'Usa la scheda Livelli per selezionare le aree attacco',
 'Userset maximum marches reached': 'Massima raggiunta Userset marce',
 'Verbose logging': 'Registrazione dettagliata',
 'waiting': 'in attesa',
 'Warnings': 'Avvertenze',
 'Wave attack to': 'Attacco Onda',
 'Wave': 'Onda',
 'Window drag': 'Trascinamento finestra con il mouse',
 '~AquaTroop':'Fang',   /* Abbreviation (max. 8 characters) */
 '~ArmoredTransport':'Bindati', /* Abbreviation (max. 8 characters) */
 '~BattleDragon':'Drg Gr',  /* idem as above */
 '~Conscript':'Conscrit',  /* idem above */
 '~FireDragon':'DrgFuoco',  /* idem */
 '~FireMirror':'Specchi',  /* idem */
 '~FireTroop':'Magma',   /* idem */
 '~Giant':'Giant',    /* idem */
 '~GreatDragon':'Grd Drg',  /* idem */
 '~Halberdsman':'Alabarde',  /* idem */
 '~Longbowman':'Arcieri',  /* idem */
 '~Minotaur':'Mino',    /* idem */
 '~PackDragon':'PckDrg',   /* idem */ 
 '~Porter':'Portiere',   /* idem */
 '~Spy':'Espía',     /* idem */
 '~StoneDragon':'DrgPierre',  /* idem */
 '~StoneTroop':'Orchi',   /* idem */
 '~SwiftStrikeDragon':'Drg Vel', /* idem */
 '~WaterDragon':'DrgAcqua',  /* idem */
 '~WindDragon':'DrgVento',  /* idem */
 '~WindTroop':'Banshee',   /* idem */
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Hollandaises (Dutch)
 *******************************************************************************/
case 'nl':
 LANG_OBJECT = {
 'above the first value':'boven de eerste waarde',
 'Action Logs': 'Actie Logs',
 'Actions': 'Acties',
 'and': 'en',
 'Are you sure you want to':'Ben je zeker dat je wilt',
 'at': 'bij',
 'Attack One Target in Waves': 'Een Aanval Doel in Golven',
 'Attack sent to': 'Aanval verzonden naar',
 'Attacking': 'Aanvallen',
 'Attacks Configuration': 'Aanvallen Configuratie',
 'Attacks Logs': 'Aanvallen Logs',
 'Attacks stopped momentarily to prevent server blocking':'Aanvallen stopte even naar de server blokkeren te voorkomen',
 'Attacks': 'Aanvallen',
 'Auto Refresh every':'Auto Vernieuwen om de',
 'Automatically': 'Automatisch',
 'Awaiting task completion notification': 'In afwachting van voltooiing van de taak melding',
 'Building': 'Bebouw',
 'Busy': 'Bezig',
 'by': 'door',
 'Clear last attack on all maps': 'Duidelijke laatste aanval op alle kaarten',
 'Clear last attack on current map': 'Duidelijke laatste aanval op de huidige kaart',
 'Config': 'Configuratie',
 'Console Logs': 'Troosten Logs',
 'Console': 'Troosten',
 'Coordinates': 'Coördinaten',
 'Days': 'Dagen',
 'Delay Between Attacks': 'Vertraging tussen de aanvallen',
 'Disabled': 'Deactiveren',
 'Distance must be between': 'Afstand moet worden tussen',
 'Distance': 'Afstand',
 'Enable': 'Inschakelen',
 'Enabled': 'Ingeschakeld',
 'Error': 'Error',
 'First value must be between': 'Eerste waarde moet tussen',
 'Game Options': 'Spelopties',
 'Going to the coords':'Gaan om de coördinaten',
 'Hiding': 'Hiding',
 'Hour': 'Uur',
 'Hours': 'Uur',
 'Info': 'Info',
 'Invalid Date From': 'Ongeldige datum Vanuit',
 'Invalid Date To': 'Ongeldige datum To',
 'Invalid delays': 'Ongeldige vertragingen',
 'Invalid number of troops': 'Ongeldig aantal troepen',
 'Invalid Range Date': 'Ongeldige Range datum',
 'Last Attack': 'Laatste Aanval',
 'Loaded': 'Geladen',
 'Logs': 'Logs',
 'Manual attack sent to': 'Handmatig Aanval verzonden naar',
 'Maximum simultaneous marches': 'Maximaal gelijktijdige marsen',
 'miles':'mijlen',
 'Minimum Housing': 'Minimum Behuizing',
 'Minimum Resource Levels': 'Minimum Hulpbron Niveaus',
 'Minutes': 'Notulen',
 'Full': 'Volledige',
 'No Generals Available': 'Geen generaals beschikbaar',
 'No targets or troops available': 'Geen targets of troepen beschikbaar zijn',
 'No troops available': 'Geen troepen beschikbaar',
 'No Troops Defined': 'Geen troepen Defined',
 'Not enough': 'Niet genoeg',
 'Not': 'Niet',
 'of inactivity':'van inactiviteit',
 'of': 'van',
 'Opening the map on the last position':'Het openen van de kaart op de laatste positie',
 'Options': 'Opties',
 'Outpost 1': 'Stad van Water',
 'Outpost 2': 'City of Stone',
 'Outpost 3': 'City of Fire',
 'Outpost 4': 'Stad van de Wind',
 'Permanent Data':'Permanente Gegevens',
 'Preparing Attack':'Voorbereiden aanval',
 'Refresh': 'Verversen',
 'Researching': 'Onderzoek',
 'Retry in':'Opnieuw in',
 'Run Time': 'Uitvoeringstijd',
 'Safe Mode': 'Veilige Modus',
 'Scanning Map': 'Scannen kaart binnen $NUM$ mijl <BR> Dit duurt ongeveer een tijd',
 'Script Options': 'Script opties',
 'Search Radius':'Zoek in een straal',
 'Second value must be at least':'Tweede waarde moet minimaal',
 'Seconds': 'Seconden',
 'Send Dragon every certain number of waves':'Stuur Dragon elk aantal Golf',
 'Start Date': 'Startdatum',
 'Stop if any troops lost': 'Stop eventueel troepen verloren',
 'Successfully': 'Succesvol',
 'Summary': 'Overzicht',
 'Targets': 'Doelen',
 'Task Completed': 'Taak voltooid',
 'Tasks': 'Taken',
 'Too many errors,  disabling auto train': 'Te veel fouten, het uitschakelen van automatische trein',
 'Too many troops for muster point level': 'Te veel troepen voor verzamelpunt niveau',
 'Training Configuration': 'Opleiding Configuratie',
 'Training queue': 'Opleiding wachtrij',
 'Troops for Wave Attack': 'Troepen voor Golfaanval',
 'Troops lost': 'Troepen verloren',
 'Troops Not Defined': 'Troepen niet gedefinieerd',
 'Use the Levels Tab to select attack areas': 'Gebruik het tabblad Niveaus om aan te vallen gebieden te selecteren',
 'Userset maximum marches reached': 'Userset maximale marsen bereikt',
 'Verbose logging': 'Uitgebreide logging',
 'waiting': 'wachten',
 'Warnings': 'Waarschuwingen',
 'Wave attack to': 'Golf te vallen',
 'Wave': 'Golf',
 'Window drag': 'Venster slepen',
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Polish
 *******************************************************************************/
case 'pl':
 LANG_OBJECT = {
 'above the first value':'powyzej pierwszej wartosci',
 'Action Logs':'Dzienniki dzialaniu',
 'Actions':'Akcje',
 'and':'i',
 'Are you sure you want to':'Czy na pewno chcesz',
 'at':'w',
 'Attack One Target in Waves':'Jeden cel ataku w Fala',
 'Attack sent to':'Wyslane do Atak',
 'Attacking':'Atak',
 'Attacks Configuration':'Ataki Konfiguracja',
 'Attacks Logs':'Logi Ataki',
 'Attacks stopped momentarily to prevent server blocking':'Ataki zatrzymal sie na chwile, aby zapobiec blokowaniu serwerów',
 'Attacks':'Ataki',
 'Auto Refresh every':'Automatyczne odswiezanie co',
 'Automatically':'Automatycznie',
 'Awaiting task completion notification':'Oczekiwanie na zakonczenie zadania zgloszeniu',
 'Building':'Budowanie',
 'Busy':'Zajety',
 'by':'prze',
 'Clear last attack on all maps':'Usun ostatnie atak na wszystkich mapach',
 'Clear last attack on current map':'Usun ostatnie atak na aktualna mape',
 'Config':'Konfiguracja',
 'Console Logs':'Dzienniki Konsola',
 'Console':'Konsola',
 'Coordinates':'Wspólrzedne',
 'Days':'Dni',
 'Delay Between Attacks':'Przerwa pomiedzy atakami',
 'Disabled':'Dezaktywowac',
 'Distance must be between':'Odleglosc powinna wynosic od',
 'Distance':'Odleglosc',
 'Enable':'Wlac',
 'Enabled':'Wlaczone',
 'Error':'Blad',
 'First value must be between':'Wartosc musi byc pierwsze entre',
 'Game Options':'Opcje gry',
 'Going to the coords':'Przechodzac do wspólrzednych',
 'Hiding':'Ukrywanie',
 'Hour':'Godziny',
 'Hours':'Godziny',
 'Info':'Informacje',
 'Invalid Date From':'Nieprawidlowe dane od',
 'Invalid Date To':'Nieprawidlowe dane do',
 'Invalid delays':'Niewazny opóznienia',
 'Invalid number of troops':'Bledna liczba zolnierzy',
 'Invalid Range Date':'Nieprawidlowy zakres dat',
 'Last Attack':'Ostatni atak',
 'Loaded':'Zaladowany',
 'Logs':'Dzienniki',
 'Manual attack sent to':'Podrecznik wyslane do ataku',
 'Maximum simultaneous marches':'Maksymalna jednoczesne marsze',
 'miles':'mil',
 'Minimum Housing':'Minimalna Obudowa',
 'Minimum Resource Levels':'Minimalnego poziomu zasobów',
 'Minutes':'Minut',
 'Full':'Pelny',
 'No Generals Available':'Niedostepny generalnych',
 'No targets or troops available':'Nie celów lub dostepnych oddzialów',
 'No troops available':'Wojsko nie jest dostepna',
 'No Troops Defined':'Nie zdefiniowane Troops',
 'Not enough':'A Malo',
 'Not':'Nie',
 'of inactivity':'bezczynnosci',
 'of':'z',
 'Opening the map on the last position':'Otwarcie mapy na ostatniej pozycji',
 'Options':'Opcje',
 'Permanent Data':'Stale Danych',
 'Preparing Attack':'Przygotowanie Atak',
 'Refresh':'Odswiez',
 'Researching':'Badania',
 'Retry in':'Ponowna próba',
 'Run Time':'Czas pracy',
 'Safe Mode':'Tryb awaryjny',
 'Scanning Map':'W $NUM$ mil Skanowanie map <BR> powinna to okolo czas',
 'Script Options':'Opcje Script',
 'Search Radius':'OdlegL',
 'Second value must be at least':'Druga wartosc musi wynosic co najmniej',
 'Seconds':'Sekund',
 'Send Dragon every certain number of waves':'Wyslij Smoka co pewnej liczby Fala',
 'Start Date':'Poczatek',
 'Stop if any troops lost':'Stop, jezeli jakiekolwiek wojska stracone',
 'Successfully':'Powodzeniem',
 'Summary':'Podsumowanie',
 'Targets':'Cele',
 'Task Completed':'Adanie Wykonane',
 'Tasks':'Zadania',
 'Too many errors,  disabling auto train':'Byt wiele bledów, wylaczenie pociagu auto',
 'Too many troops for muster point level':'Byt wielu zolnierzy zebrac punkt za poziom',
 'Training Configuration':'Konfiguracja Szkolenia',
 'Training queue':'Szkolenia kolejki',
 'Troops for Wave Attack':'Fala Atak wojsk',
 'Troops lost':'Wojsko Stracone',
 'Troops Not Defined':'Wojsko Nie zdefiniowane',
 'Use the Levels Tab to select attack areas':'Uzyciu karty Poziom wybrac obszary atak',
 'Userset maximum marches reached':'Maksymalnie marsze Zasieg UserSet',
 'Verbose logging':'Verbose logging',
 'waiting':'czeka',
 'Warnings':'Ostrzezenia',
 'Wave attack to':'Atak Fala',
 'Wave':'Fala',
 'Window drag':'Okna przeciagnij',
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Russian
 *******************************************************************************/
case 'ru':
case 'tt':
 LANG_OBJECT = {
 'above the first value':'выше первого значения',
 'Action Logs':'Действие Журналы',
 'Actions':'Действия',
 'and':'и',
 'Are you sure you want to':'Вы уверены, что хотите',
 'at':'в',
 'Attack One Target in Waves':'Атака одна цель в волнах',
 'Attack sent to':'Атака направлены',
 'Attacking':'Атака',
 'Attacks Configuration':'Атаки Конфигурация',
 'Attacks Logs':'Атаки Журналы',
 'Statistics':'статистика',
 'Attacks stopped momentarily to prevent server blocking':'Атаки на мгновение остановился, чтобы предотвратить блокировку сервера',
 'Attacks':'Атака',
 'Auto-Collection of Resources':'Авто урожая ресурсов из форпостов каждый',
 'Auto Refresh every':'Автоматическое обновление каждые',
 'Automatically':'Автоматически',
 'Awaiting task completion notification':'Ожидающие уведомления завершения задачи',
 'Battle Report':'Битва отчетов',
 'Building':'Строительство',
 'Busy':'Занят',
 'by':'на',
 'Clear last attack on all maps':'Очистить последнюю атаку на всех картах',
 'Clear last attack on current map':'Очистить последнюю атаку на текущую карту',
 'Config':'Config',
 'Console Logs':'Консоль Журналы',
 'Console':'Консоль',
 'Coordinates':'Координаты',
 'Days':'Дней',
 'Delay Between Attacks':'Задержка между атаками',
 'Disabled':'инвалидов',
 'Distance must be between':'расстояние должно быть между',
 'Distance':'Расстояние',
 'Enable':'Включить',
 'Enabled':'Enabled',
 'Error':'Ошибка',
 'First value must be between':'Первое значение должно быть между',
 'Game Options':'Game Options',
 'My Generals':'Генералы',
 'Going to the coords':'Переходя к координатам',
 'Hiding':'Сокрытие',
 'Hour':'Час',
 'Hours':'Часы',
 'Currently Inactive':'простоя',
 'Info':'Информация',
 'Invalid Date From':'Неверный Дата С',
 'Invalid Date To':'Неверный Дата To',
 'Invalid delays':'Неверный задержки',
 'Invalid number of troops':'Неверное число войск',
 'Invalid Range Date':'Неверный диапазон дат',
 'Last Attack':'Последнее Атака',
 'Loaded':'Loaded',
 'Logs':'Журналы',
 'Manual attack sent to':'Руководство атаки направлены',
 'Maximum simultaneous marches':'Максимально одновременных маршей',
 'miles':'миль',
 'Minimum Housing':'Минимальное жилье',
 'Minimum Resource Levels':'минимальный уровень ресурсов',
 'Minutes':'Minutes',
 'Muster Point':'Muster центр',
 'Full':'Полное',
 'Required':'Вам нужно',
 'No Generals Available':'Нет генералов Доступный',
 'No targets or troops available':'Нет цели или войск, доступных',
 'No troops available':'Нет доступных войск',
 'No Troops Defined':'Нет войск Определено',
 'Not enough':'Не хватает',
 'Not':'Не',
 'of inactivity':'бездействия',
 'of':'в',
 'Opening the map on the last position':'Открытие карты на последней позиции',
 'Options':'опции',
 'Permanent Data':'Постоянный данных',
 'Preparing Attack':'Подготовка атаки',
 'Refresh':'Обновить',
 'Researching':'Исследование',
 'Retry in':'Повтор в',
 'Run Time':'Run Time',
 'Safe Mode':'Безопасный режим',
 'Scanning Map':'Сканирование карты в течение $NUM$ миль <BR> Это должно занять около время',
 'Script Options':'Сценарий опции',
 'Search Radius':'Поиск Радиус',
 'Second value must be at least':'Второе значение должно быть не менее',
 'Seconds':'секунды',
 'Send Dragon every certain number of waves':'Отправить Дракон каждого конкретного числа волн',
 'Start Date':'Дата начала',
 'Stop if any troops lost':'Остановитесь, если какой-либо войска потеряли',
 'Successfully':'Успешно',
 'Summary':'Сводка',
 'Targets':'Цели',
 'Task Completed':'Задача выполнена',
 'Tasks':'Задачи',
 'Too many errors,  disabling auto train':'Слишком много ошибок, отключение автоматического поезд',
 'Too many troops for muster point level':'Слишком много войск для набраться уровня точки',
 'Training Configuration':'Обучение Конфигурация',
 'Training queue':'Обучение очередь',
 'Troops for Wave Attack':'Войска для волны атаки',
 'Troops lost':'Войска потеряли',
 'Troops Not Defined':'Войска Не определен',
 'Use the Levels Tab to select attack areas':'Использование уровней Tab, чтобы выбрать уязвимую зону',
 'Userset maximum marches reached':'Userset максимальной марши достигли',
 'Verbose logging':'ведение подробного журнала',
 'waiting':'ожидание',
 'Warnings':'Предупреждения',
 'Wave attack to':'Волна атака',
 'Wave':'Волна',
 'Window drag':'окно перетащить',
 '~Zzz':'Zzz'
 };
 break;
 /*******************************************************************************
  Turkish
 *******************************************************************************/
case 'tr':
case 'tk':
 LANG_CODE = 'tr';
 LANG_OBJECT = {
 'above the first value':'Ilk degerin üstünde',
 'Action Logs':'Eylem Kayitlar',
 'Actions':'Eylemler',
 'and':'ve',
 'Are you sure you want to':'Istediginiz emin',
 'at':'az',
 'Attack One Target in Waves':'Dalgalari Bir Hedef Saldiri',
 'Attack sent to':'Saldiri gönderildi',
 'Attacking':'Saldirmak',
 'Attacks Configuration':'Yapilandirma Saldirilari',
 'Attacks Logs':'Saldirilar Kayitlar',
 'Statistics':'Istatistik',
 'Attacks stopped momentarily to prevent server blocking':'Saldirilari engelleme sunucu önlemek için bir an durdu',
 'Attacks':'Saldirilar',
 'Auto-Collection of Resources':'Karakollarini Otomatik hasat kaynaklari her',
 'Auto Refresh every':'Otomatik Yenileme her',
 'Automatically':'Otomatik',
 'Awaiting task completion notification':'Bekliyor görev tamamlama bildirimi',
 'Battle Report':'Raporlari Sil',
 'Building':'Bina',
 'Busy':'Mesgul',
 'by':'ile',
 'Clear last attack on all maps':'Tüm haritalarda açik son saldiri',
 'Clear last attack on current map':'Mevcut harita üzerinde net son saldiri',
 'Config':'Yapilandirma',
 'Console Logs':'Konsol Kayitlar',
 'Console':'Konsol',
 'Coordinates':'Koordinatlar',
 'Days':'Günleri',
 'Delay Between Attacks':'Saldirilar Arasindaki Gecikme',
 'Disabled':'Engelli',
 'Distance must be between':'Mesafe arasinda olmalidir',
 'Distance':'Mesafe',
 'Enable':'Etkinlestir',
 'Enabled':'Etkin',
 'Error':'Hata',
 'First value must be between':'Ilk degeri arasinda olmalidir',
 'Game Options':'Oyun Seçenekleri',
 'My Generals':'Generaller',
 'Going to the coords':'Koordinatlari gitmek',
 'Hiding':'Gizleme',
 'Hour':'Saat',
 'Hours':'Saat',
 'Currently Inactive':'bosta',
 'Info':'Bilgi',
 'Invalid Date From':'Geçersiz Tarih',
 'Invalid Date To':'Geçersiz Tarih',
 'Invalid delays':'Geçersiz gecikmeler',
 'Invalid number of troops':'Geçersiz asker sayisi',
 'Invalid Range Date':'Geçersiz Araligi Tarihi',
 'Last Attack':'Son Saldiri',
 'Loaded':'Yüklü',
 'Logs':'Kayitlar',
 'Manual attack sent to':'Ile gönderilen Manuel saldiri',
 'Maximum simultaneous marches':'Maksimum eszamanli yürüyüslerle',
 'miles':'mil',
 'Minimum Housing':'Asgari Konut',
 'Minimum Resource Levels':'Asgari Kaynak Seviyeleri',
 'Minutes':'Dakika',
 'Full':'Tam',
 'Muster Point':'Nokta Muster',
 'Required':'Gerek',
 'No Generals Available':'Hayir Generals kullanilabilir',
 'No targets or troops available':'Yok hedefler veya asker',
 'No troops available':'Yok askerlerinin',
 'No Troops Defined':'Askerler Tanimli',
 'Not enough':'Yeterli degil',
 'Not':'Degil',
 'of inactivity':'hareketsizlik',
 'of':',',
 'Opening the map on the last position':'Son konumu harita açma',
 'Options':'Seçenekler',
 'Outpost 1':'Su Sehri',
 'Outpost 2':'Tas Sehir',
 'Outpost 3':'Ates Sehir',
 'Outpost 4':'Rüzgar Sehir',
 'Permanent Data':'Kalici Veri',
 'Preparing Attack':'Saldiri hazirlanmasi',
 'Refresh':'Yenile',
 'Researching':'Arastirma',
 'Run Time':'Çalisma Süresi',
 'Safe Mode':'Güvenli Mod',
 'Scanning Map':'Içinde $NUM$ kilometre <BR> Tarama harita bu yaklasik bir dakika zaman',
 'Script Options':'Komut Seçenekleri',
 'Search Radius':'Arama yariçapi',
 'Second value must be at least':'Ikinci deger olmali, en azindan',
 'Seconds':'Degil',
 'Send Dragon every certain number of waves':'Ejderha dalgalarin her belirli sayida Dalga',
 'Start Date':'Baslangiç ??Tarihi',
 'Stop if any troops lost':'Herhangi bir asker kaybetti Durdur',
 'Successfully':'Basariyla',
 'Summary':'Özet',
 'Targets':'Hedefler',
 'Task Completed':'Görev Tamamlandi',
 'Tasks':'Görevler',
 'Too many errors,  disabling auto train':'Çok fazla hata, otomatik tren devre disi birakma',
 'Too many troops for muster point level':'Görememesi noktasi seviyesi için çok sayida asker',
 'Training Configuration':'Egitim Yapilandirma',
 'Training queue':'Egitim kuyruk',
 'Troops for Wave Attack': 'Dalgalanma asker Saldirilari',
 'Troops lost':'Askerler kaybetti',
 'Troops Not Defined':'Askerler Tanimli degil',
 'Use the Levels Tab to select attack areas':'Saldiri alanlarini seçmek için Seviyeleri Sekmesini kullanin',
 'Userset maximum marches reached':'Ayarlidir maksimum ulasti yürüyüsleri',
 'Verbose logging':'Günlügü etkinlestir',
 'waiting':'bekleyen',
 'Warnings':'Uyarilar',
 'Wave attack to':'Dalga saldiri',
 'Wave':'Dalga',
 'Window drag':'Sürükleme etkinlestirin',
 '~Zzz':'Zzz'
 };
 break;
default:
 LANG_OBJECT = {
 'Scanning Map':'Scanning map within $NUM$ miles<BR>This should take about a time',
 '~AquaTroop':'Fang',   /* Abbreviation (max. 8 characters) */
 '~ArmoredTransport':'ArmTrans', /* Abbreviation (max. 8 characters) */
 '~BattleDragon':'Bat Drg',  /* idem as above */
 '~Conscript':'Conscr',   /* idem above */
 '~FireDragon':'Fire Drg',  /* idem */
 '~FireMirror':'Fire Mir',  /* idem */
 '~FireTroop':'Magma',   /* idem */
 '~Giant':'Giant',    /* idem */
 '~GreatDragon':'Grt Drg',  /* idem */
 '~Halberdsman':'Halbrd',  /* idem */
 '~Longbowman':'LB Man',   /* idem */
 '~Minotaur':'Mino',    /* idem */
 '~PackDragon':'PckDrg',   /* idem */ 
 '~Porter':'Porter',    /* idem */
 '~Spy':'Spy',     /* idem */
 '~StoneDragon':'Stn Drg',  /* idem */
 '~StoneTroop':'Ogre',   /* idem */
 '~SwiftStrikeDragon':'SS Drg', /* idem */
 '~WaterDragon':'Wat Drg',  /* idem */
 '~WindDragon':'Wnd Drg',  /* idem */
 '~WindTroop':'Banshee',   /* idem */
 '~Zzz':'Zzz'
 };
}

 /********************************************************************************
* All id and class names must be scrambled to prevent the script from being    *
* blocked. These names have to be generated and allocated to CSS prior to      *
* rest of the script being initialised.                                        *
*                                                                              *
* Class List is an array containing the normal names for each class. This       *
* is then looped through and then scrambled to generate a unique name. A check *
* is done to ensure no two randmised names are the same before allowing the    *
* script to continue.                                                          *
********************************************************************************/ 
// Class List
$J.each([
   'bnt_blue'
  ,'bnt_cyan'
  ,'bnt_green'
  ,'btn_on'
  ,'btn_off'
  ,'bnt_red'
  ,'bnt_purple'
  ,'bnt_red'
  ,'bnt_yellow'
  ,'bold_red'
  ,'compact_table'
  ,'content'
  ,'defending'
  ,'hiding'
  ,'hide_inputbox'
  ,'main-box'
  ,'march_camp'
  ,'march_wave'
  ,'row_headers'
  ,'row_top_headers'
  ,'scrollable'
  ,'status_feedback'
  ,'status_report'
  ,'status_ticker'
  ,'subtitle'
  ,'support_link'
  ,'table'
  ,'table_headers'
  ,'title'
 ], function(key, value){ setUID(value); });

/** Add CSS Styles 
******************************/

$J("<style>").append('\
 .jewel {\
  padding : 1px;\
  font-size: 8pt !important;\
  color: #666 !important;\
 }\
 div.short {\
  height:7px;\
 }\
 .' + UID['hiding'] + ' {\
  background-color: rgb(0,160,110);\
  color: white;\
  padding-left: 10px;\
  padding-right: 10px;\
  margin-right: -2px;\
  border-radius: 2px;\
  -moz-border-radius: 2px;\
  -webkit-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
  -moz-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
 }\
 .' + UID['defending'] + ' {\
  background-color: rgb(184,0,46);\
  color: white;\
  padding-left: 10px;\
  padding-right: 10px;\
  margin-right: -2px;\
  border-radius: 2px;\
  -moz-border-radius: 2px;\
  -webkit-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
  -moz-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
 }\
 .' + UID['scrollable'] + ' {\
  overflow: auto !important;\
 }\
 .' + UID['main-box'] + ' .ui-dialog-content {\
  padding:2px !important;\
  overflow:hidden !important;\
 }\
 .' + UID['main-box'] + ' ul.tabs {\
  overflow: hidden;\
  display: block;\
  border-bottom: 1px solid #898989;\
  height: 25px;\
  list-style: none;\
  margin: 0;\
  padding: 0;\
 }\
 .' + UID['main-box'] + ' ul.tabs li.tab {\
  display: inline-block;\
  float: left;\
  cursor:pointer !important;\
 }\
 .' + UID['main-box'] + ' ul.tabs li.tab a {\
  background-color: rgb(235,238,245);\
  border-bottom: 0;\
  border: 1px solid #898989;\
  border-left-width: 0;\
  color: #333;\
  font-weight: bold;\
  display: block;\
  height: 16px;\
  margin-top: 6px;\
  padding: 2px 9px 3px 8px;\
  position: relative;\
  text-decoration: none;\
  cursor:pointer;\
 }\
 .' + UID['main-box'] + ' ul.tabs li.first a {\
  border-left-width: 1px;\
 }\
 .' + UID['main-box'] + ' ul.tabs li.tab a.selected {\
  background-color: rgb(60,90,150);\
  border-top-color: #3B5998;\
  border-bottom-color: #3B5998;\
  border-left-color: #5973A9;\
  border-right-color: #5973A9;\
  color: white;\
  -webkit-box-shadow: rgba(0,0,0,0.6) 1px 0px 1px;\
  -moz-box-shadow: rgba(0,0,0,0.6) 1px 0px 1px;\
  background-image: linear-gradient(bottom, rgba(0,0,0,0) 12%, rgba(255,255,255,0.3) 90%, rgba(255,255,255,0.9) 99%);\
  background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0) 12%, rgba(255,255,255,0.3) 90%, rgba(255,255,255,0.9) 99%);\
  background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,0) 12%, rgba(255,255,255,0.3) 90%, rgba(255,255,255,0.9) 99%);\
 }\
 .' + UID['main-box'] + ' div.container {\
  height: auto;\
  width: 100%;\
  overflow-x: auto;\
 }\
 .' + UID['main-box'] + ' div.container ul.tabs li.tab a {\
  height: 13px;\
  background-color: rgb(241,241,241);\
 }\
 .' + UID['main-box'] + ' div.container ul.tabs li.tab a.selected {\
  background-color: rgb(110,132,181);\
 }\
 .' + UID['title'] + ' {\
  border:1px solid;\
  border-color:#ffffff;\
  font-weight:bold;\
  padding-top:2px;\
  padding-bottom:2px;\
  text-align:center;\
  color:#ffffff;\
  background-color:rgb(60,90,150);\
  border-radius: 2px;\
  -moz-border-radus: 2px;\
 }\
 .' + UID['main-box'] + ' .ui-dialog-title *,\
 .' + UID['title'] + ' * {\
  display:inline-block !important;\
  font-style:normal !important;\
  font-weight:bold;\
  color:#ffffff !important;\
  line-height:10pt !important;\
  text-decoration:none !important;\
  padding:0;\
 }\
 .' + UID['subtitle'] + ' {\
  border:1px solid;\
  border-color:#ffffff;\
  font-weight:bold;\
  padding-top:2px;\
  padding-bottom:2px;\
  text-align:center;\
  color:#ffffff;\
  background-color: rgb(60,60,60);\
  border-radius: 2px;\
  -moz-border-radus: 2px;\
 }\
 .' + UID['content'] + ' {\
  border:1px solid rgb(255,200,50);\
  background-color: rgb(245,245,228);\
  padding:3px;\
  border-radius: 2px;\
  -moz-border-radus: 2px;\
  -webkit-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
  -moz-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
 }\
 .' + UID['status_ticker'] + ' {\
  border:1px solid #995;\
  background-color: rgb(239,239,224);\
  padding:2px;\
  border-radius: 2px;\
  -moz-border-radus: 2px;\
  -webkit-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
  -moz-box-shadow: rgba(0,0,0,0.52) 0 0 2px;\
 }\
 .' + UID['status_report'] + ' {\
  height: 106px;\
  max-height: 106px;\
  overflow:auto;\
 }\
 .' + UID['status_feedback'] + ' {\
  border: 1px solid #ddd;\
  padding-top: 5px;\
  padding-right: 5px;\
  padding-bottom: 0.5em;\
  padding-left: 5px;\
  height: 34px;\
  background-color: rgb(255,235,235);\
  text-align:left;\
  font-weight:bold;\
  border-radius: 3px;\
  -moz-border-radius: 3px;\
 }\
 table.' + UID['table'] + ' tr td,\
 table.' + UID['compact_table'] + ' tr td {\
  border:none;\
  background:none;\
  white-space:nowrap;\
  padding: 1px 1px;\
  cursor: default;\
 }\
 table.' + UID['hide_inputbox'] + ' tr td {\
  padding-bottom: 0px;\
  padding-right: 0px;\
 }\
 table.' + UID['table'] + ' tr td {\
  padding: 1px 4px;\
 }\
 table.' + UID['table'] + ' tr td.left,\
 table.' + UID['compact_table'] + ' tr td.left,\
  font-weight:bold;\
  text-align:right;\
  padding-right: 5px;\
 }\
 table.' + UID['table_console'] + ' tr td {\
  white-space:normal;\
  vertical-align:top;\
 }\
 td.' + UID['underline'] + ' {\
  border-bottom:1px solid #ccc;\
  background:none;\
  padding: 1px 4px 1px 4px;\
 }\
 table tr.' + UID['row_top_headers'] + ' td,\
 table tr.' + UID['row_headers'] + ' td {\
  color: white;\
  background-color: rgb(110,115,125);\
  border-right: 2px solid #eef;\
  font-weight:bold;\
  text-align:center;\
  line-height:11pt;\
 }\
 table tr.' + UID['row_top_headers'] + ' td {\
  background-color: rgb(90,95,115);\
 }\
 tr.' + UID['row_marchOther'] + ' td {\
  color:#888888;\
 }\
 tr.' + UID['row_marchMine'] + ' td {\
  color:#000000;\
 }\
 tr.' + UID['row_owned'] + ' {\
 }\
 input.' + UID['btn_on'] + ',\
 input.' + UID['btn_off'] + ',\
 input.' + UID['bnt_red'] + ',\
 input.' + UID['bnt_green'] + ',\
 input.' + UID['bnt_blue'] + ',\
 input.' + UID['bnt_yellow'] + ',\
 input.' + UID['bnt_cyan'] + ',\
 input.' + UID['bnt_purple'] + ',\
 .' + UID['main-box'] + ' input[type=button] {\
  width:130px;\
  padding-top:1px;\
  padding-bottom:1px;\
  color:white;\
  font-weight:bold;\
  border: 1px solid #333;\
  border-radius: 3px;\
  -moz-border-radius: 3px;\
  background-image: linear-gradient(bottom, rgba(0,0,0,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.5) 99%);\
  background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.5) 99%);\
  background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.5) 99%);\
  -webkit-box-shadow: rgba(0,0,0,0.52) 1px 1px 1px;\
  -moz-box-shadow: rgba(0,0,0,0.52) 1px 1px 1px;\
  cursor:hand;\
  cursor:pointer;\
 }\
 .' + UID['main-box'] + ' input[type=button] {\
  background-color: rgb(0,125,189);\
 }\
 .' + UID['main-box'] + ' input[type=button]:hover {\
  background-color: rgb(40,150,210);\
 }\
 input.' + UID['btn_on'] + ' {\
  background-color: rgb(0,160,110) !important;\
 }\
 input.' + UID['btn_on'] + ':hover {\
  background-color: rgb(0,200,150) !important;\
 }\
 input.' + UID['btn_off'] + ' {\
  background-color: rgb(184,0,46) !important;\
 }\
 input.' + UID['btn_off'] + ':hover {\
  background-color: rgb(200,50,100) !important;\
 }\
 input.thin {\
  width: auto !important;\
  margin:0;\
  padding-top:0;\
  padding-bottom:0;\
  padding-left:2px;\
  padding-right:2px;\
  font-size:10px;\
 }\
 input.short {\
  width:30px !important;\
 }\
 input.' + UID['bnt_red'] + ' {\
  background-color: rgb(184,0,46) !important;\
 }\
 input.' + UID['bnt_red'] + ':hover {\
  background-color: rgb(200,50,100) !important;\
 }\
 input.' + UID['bnt_green'] + ' {\
  background-color: rgb(0,160,110) !important;\
 }\
 input.' + UID['bnt_green'] + ':hover {\
  background-color: rgb(0,210,150) !important;\
 }\
 input.' + UID['bnt_blue'] + ' {\
  background-color: rgb(0,94,189);\
 }\
 input.' + UID['bnt_blue'] + ':hover {\
  background-color: rgb(0,125,150);\
 }\
 input.' + UID['bnt_yellow'] + ' {\
  background-color:#BFBF00 !important;\
 }\
 input.' + UID['bnt_yellow'] + ':hover {\
  background-color:#DFDF00 !important;\
 }\
 input.' + UID['bnt_cyan'] + ' {\
  background-color:#00BFBF !important;\
 }\
 input.' + UID['bnt_cyan'] + ':hover {\
  background-color:#00DFDF !important;\
 }\
 input.' + UID['bnt_purple'] + ' {\
  background-color:#BF00BF !important;\
 }\
 input.' + UID['bnt_purple'] + ':hover {\
  background-color:#DF00DF !important;\
 }\
 .' + UID['main-box'] + ' input[type=text] {\
  border: 1px solid #888;\
  border-radius: 2px;\
  text-align: right;\
  -moz-border-radius: 2px;\
  -webkit-box-shadow: rgba(0,0,0,0.2) 1px 1px 3px inset;\
  -moz-box-shadow: rgba(0,0,0,0.2) 1px 1px 3px inset;\
 }\
 .' + UID['main-box'] + ' input[type=text]:active,\
 .' + UID['main-box'] + ' input[type=text]:focus {\
  border-color: #000;\
  -webkit-box-shadow: rgba(0,0,0,0.5) 1px 1px 4px inset;\
  -moz-box-shadow: rgba(0,0,0,0.5) 1px 1px 4px inset;\
 }\
 .' + UID['hide_inputbox'] + ' input[type=text] {\
  border: 1px solid rgba(0,0,0,0.4);;\
  background-color: rgba(255,255,255,0.3);\
  -webkit-box-shadow: rgba(0,0,0,0.2) 1px 1px 3px inset;\
  -moz-box-shadow: rgba(0,0,0,0.2) 1px 1px 3px inset;\
 }\
 .' + UID['main-box'] + ' select {\
  margin : 0 !important;\
 }\
 .' + UID['bold_red'] + ' {\
  color:#550000;\
  font-weight:bold;\
 }\
 hr.thin {\
  margin:0px;\
  padding:0px;\
 }\
 ').appendTo("head");


 
 
/** Add jQuery UI CSS Styles 
******************************/
$J("<style>").append('\
 /* jQuery UI CSS Framework 1.8.16 */\
 /* Layout helpers\
 ----------------------------------*/\
 .ui-helper-hidden\
 {\
  display: none;\
 }\
 .ui-helper-hidden-accessible\
 {\
  position: absolute !important;\
  clip: rect(1px 1px 1px 1px);\
  clip: rect(1px,1px,1px,1px);\
 }\
 .ui-helper-reset\
 {\
  margin: 0;\
  padding: 0;\
  border: 0;\
  outline: 0;\
  line-height: 1.3;\
  text-decoration: none;\
  font-size: 100%;\
  list-style: none;\
 }\
 .ui-helper-clearfix:after\
 {\
  content: ".";\
  display: block;\
  height: 0;\
  clear: both;\
  visibility: hidden;\
 }\
 .ui-helper-clearfix\
 {\
  display: inline-block;\
 }\
 /* required comment for clearfix to work in Opera \*/\
 * html .ui-helper-clearfix\
 {\
  height:1%;\
 }\
 .ui-helper-clearfix\
 {\
  display:block;\
 }\
 /* end clearfix */\
 .ui-helper-zfix\
 {\
  width: 100%;\
  height: 100%;\
  top: 0;\
  left: 0;\
  position: absolute;\
  opacity: 0;\
  filter:Alpha(Opacity=0);\
 }\
 /* Interaction Cues\
 ----------------------------------*/\
 .ui-state-disabled\
 {\
  cursor: default !important;\
 }\
 /* Icons\
 ----------------------------------*/\
 /* Misc visuals\
 ----------------------------------*/\
 /* Overlays */\
 .ui-widget-overlay\
 {\
  position: absolute;\
  top: 0;\
  left: 0;\
  width: 100%;\
  height: 100%;\
 }\
 /* states and images */\
 .ui-icon\
 {\
  display: block;\
  text-indent: -99999px;\
  overflow: hidden;\
  background-repeat: no-repeat;\
 }\
 /* Component containers\
 ----------------------------------*/\
 .ui-widget\
 {\
  font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;\
  font-size: 0.9em;\
 }\
 .ui-widget .ui-widget\
 {\
  font-size: 0.9em;\
 }\
 .ui-widget input,\
 .ui-widget select,\
 .ui-widget textarea,\
 .ui-widget button\
 {\
  font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;\
  font-size: 0.9em;\
 }\
 .ui-widget-content\
 {\
  border: 1px solid #dddddd;\
  background-color: #eeeeee;\
  color: #333333;\
 }\
 .ui-widget-content a\
 {\
  color: #333333;\
 }\
 .ui-widget-header\
 {\
  border: 1px solid #000000;\
  background-color: #303030;\
  color: #ffffff;\
  font-weight: bold;\
 }\
 .ui-widget-header a\
 {\
  color: #ffffff;\
 }\
 /* Interaction states\
 ----------------------------------*/\
 .ui-state-default,\
 .ui-widget-content .ui-state-default,\
 .ui-widget-header .ui-state-default\
 {\
  border: 1px solid #cccccc;\
  background-color: #f6f6f6;\
  font-weight: bold;\
  color: #1c94c4;\
 }\
 .ui-state-default a,\
 .ui-state-default a:link,\
 .ui-state-default a:visited\
 {\
  color: #1c94c4;\
  text-decoration: none;\
 }\
 .ui-state-hover,\
 .ui-widget-content .ui-state-hover,\
 .ui-widget-header .ui-state-hover,\
 .ui-state-focus,\
 .ui-widget-content .ui-state-focus,\
 .ui-widget-header .ui-state-focus\
 {\
  border: 1px solid #fbcb09;\
  background-color: #fdf5ce;\
  font-weight: bold;\
  color: #c77405;\
 }\
 .ui-state-hover a,\
 .ui-state-hover a:hover\
 {\
  color: #c77405;\
  text-decoration: none;\
 }\
 .ui-state-active,\
 .ui-widget-content .ui-state-active,\
 .ui-widget-header .ui-state-active\
 {\
  border: 1px solid #fbd850;\
  background-color: #ffffff;\
  font-weight: bold;\
  color: #eb8f00;\
 }\
 .ui-state-active a,\
 .ui-state-active a:link,\
 .ui-state-active a:visited\
 {\
  color: #eb8f00;\
  text-decoration: none;\
 }\
 .ui-widget :active\
 {\
  outline: none;\
 }\
 /* Interaction Cues\
 ----------------------------------*/\
 .ui-state-highlight,\
 .ui-widget-content .ui-state-highlight,\
 .ui-widget-header .ui-state-highlight\
 {\
  border: 1px solid #fed22f;\
  background-color: #ffe45c;\
  color: #363636;\
 }\
 .ui-state-highlight a,\
 .ui-widget-content .ui-state-highlight a,\
 .ui-widget-header .ui-state-highlight a\
 {\
  color: #363636;\
 }\
 .ui-state-error,\
 .ui-widget-content .ui-state-error,\
 .ui-widget-header .ui-state-error\
 {\
  border: 1px solid #cd0a0a;\
  background-color: #b81900;\
  color: #ffffff;\
 }\
 .ui-state-error a,\
 .ui-widget-content .ui-state-error a,\
 .ui-widget-header .ui-state-error a\
 {\
  color: #ffffff;\
 }\
 .ui-state-error-text,\
 .ui-widget-content .ui-state-error-text,\
 .ui-widget-header .ui-state-error-text\
 {\
  color: #ffffff;\
 }\
 .ui-priority-primary,\
 .ui-widget-content .ui-priority-primary,\
 .ui-widget-header .ui-priority-primary\
 {\
  font-weight: bold;\
 }\
 .ui-priority-secondary,\
 .ui-widget-content .ui-priority-secondary,\
 .ui-widget-header .ui-priority-secondary\
 {\
  opacity: .7;\
  filter:Alpha(Opacity=70);\
  font-weight: normal;\
 }\
 .ui-state-disabled,\
 .ui-widget-content .ui-state-disabled,\
 .ui-widget-header .ui-state-disabled\
 {\
  opacity: .35;\
  filter:Alpha(Opacity=35);\
  background-image: none;\
 }\
 /* Icons\
 ----------------------------------*/\
 /* states and images */\
 .ui-icon,\
 .ui-widget-content .ui-icon\
 {\
  width: 16px;\
  height: 16px;\
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAADwCAMAAADYSUr5AAAA7VBMVEUkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiQkIiTww4gUAAAATnRSTlMAGBAyBAhQv4OZLiJUcEBmYBoSzQwgPBZCSEoeWiYwUiyFNIeBw2rJz8c4RBy9uXyrtaWNqa2zKP2fJO8KBgKPo2KVoa9s351GPm5+kWho0kj9AAAPhUlEQVR4nO1djWLbthEGyUiq5YSSLXtp7FpLOmfzkmxr126tmi2p03RJ1/Xe/3EGgARxPyAgRbIk2/hkSz4CJO4+HsE7AJSVysjI2AMUUOxahZ2iANhzBtZWr4BoIRSYAVN5u4QwDwQDRbcwfUi5KS3wFuDmFnQLa4Dtb//cqktwD5QEFFwfUs7PoCCA7y4bEJVFizcIob8KmhAplwwqVjt+9FBl3uINQniwEiryEyw9JHqGpQdEFNi+B4QQ7QOiHhysIPoAxUqxvdvvA9K42bsAv4S2fxfYOe57IJSRkZGRkZGxx7jxSHDHcRBXQMTyIjInBgHwBJ/bEx8PEANC+uhbpSSggCBAVODVabpI1S/k4WLZpTn6NpMhoX9Y40hxYERFpMcqUs4AloCtDQdID1YhnyXZ2hLjAYWiO9Dy1PDB7tPhIqLx+uMB8grZaR+Qxl2/C2RkZGRkZGRk7A7rBf7J0DR5/LUTjzUPIPSPGvQJiVJiB7kcQCiUOJrcFNtDZIf2xarQ3aGvLNxAVIFAabz90BFiBIlycTBhgWwOWCH0FLYHlPqwHaCvcIn2ZbosCevfPTRiFFcgvHukCjWwrc3GrGh1fsAof8EaUReKXkCB4/MzFNo97qLpFiKFYv/kNR5YQxQbQEofkZ2OuEOHqqT6gFTpru8CN7x/+jaZkZGRkZGRcV+x/rLUNcMMqUAscgnFocmpqkTzqymwVAPxfJ5PnIUUQOUKT04tEdWZyv3JCQSn96WS4pD97QfyW25A7NhSAbyhmVj0FEltA4vdiygBibXhoUYgykCUP7HwPTDeEqAIcHVMkZg7Zx4k0uFANs63hPQXCoRLAwdgGsr9Az7Qv7sgQGgg1aPl/BJLExBWgG4RFRLFImGmIquPC/klEGyCG0AuAXaJJC+B8FVe9NYQDEcXB8g6AQcjYJ1goJIggHWCrFR0S6kRHN5+4BzFi8NaoN35NRxUvL+JJdZr7PV4wK6fj8nIyMjIyNhr3OxdXAYq7FHZwB6bDSzSh4sF0utChqo0NAvaT1hLzXwFinmCzmeDucEQK18TTaQoFgP7bNC+RZ4OT4T6gQogDFYk+1QxQlj19QGSAWKiLYp8P0Ag1Gbz1ULfWHLg9iUnQNK5QQJcukm04blKLH2GgEJCY+HzXAZWCvHKco3Bp6MIaCjSXXRJyOxeqhnzEaF93MfFGW/O16ZvDL5TM4MJIjujz/cHypkQuuzRwWJ93BKdIt+wCRAPl9kpe2Ikkb2mFgGlxh/i40d3EHfdvoyMjIyMu43ylt/IAmGHnN5iIt7wKfbv01RAcJqFRl9lcjYQSnbQqKgC4fYOwSJt6N6trE0twZ9kN/PqNpTQeICvr4TLsDYC06U7BMjshS+v1/aT7IwQYD5LcgRQXMT2FrBfBLjZ6151jDElk9tPFfpUgk2yregusX25BJbwAFEfM+YI6vGAti4bTtizB+TjfQCrERyhKb2X8D6A9wX75P4t4neBYJeP6pdhg/gQl8MWvytzeSTjgOQBynQdh/iXKdxOrGJ/RkZGRsb9QmXihGr5+g8GGg9uTh+KoVZuNIzV+CwRucFBEyr1mVjx4irOxwM1BhirB6Q+2eNQi4eqR+aF6mELtoMzCR7V9RAFe/ZvQogNiyY8FPSUTFsLp8TeTmMui5mtw7bcaT0Yw2AA4wFRQIlkgq+1DQrNhkmoxS5Jq+u6bMAIGRECEANgXHTgWzwgBOhDH2l0oTQ4D8D5NMktBgNywAEMjo8rwATMZrPY7JGxBoJCkIBDQiAY09EGTUiBCWkUpISfGPR5AAwBfZiG2z7Ayc1yeKTxid39xBNwfHr4O0LA48ePFTvhYrF1r4tyAoz9n2MCqEuBtp/6GDR0oAYfG/R6wJExHYZHfhygsv7fEWCOj4bYmsP5A+pL4MkTfAnMlD4F+r3bobKvTyTA2P/w7PN+Agq2QW8piqMCpTBwenoKvX0AHGkGtP2YAPvTEWA7QUTAudn7/NxtOG46wWNmDtpBEkBzN7rBEvAFHp+YTB/q97qPAN4gHFqgBi8uLsC7qPCA6mg41G/+ErByPwEXDdoNxRhOx+M5jPEzQugS0ht+b1/Y3gEnYMAIAOIBE29/hIDucE8tmMsNOgK4B1RHFu4UCRlMHzv0xzcajcfdXWDs2h8TArBCkoDUJYDLmz6w7ip3BFS0ve5wTRwAn6keMA9I3QYbfSZ0DKbyt+7OXjGI1idPcfNyAyfAMlCrzaGqphYrxHocLHRJVycnfGUcbtT+jIyMjIw9x7Nn8fJSzG0TmFtO8rZT+XT3S3ub+tKJbbLd5diTVp50+zahyeHSslJ/YPrU0fuazrZO2CZ92/ZCCVXlGRiZKPJyPPRxyIFWeXLQBXJBKiq/3divEAN6ZwM200Qjm7EJBZeWm/PRWVCbYK7s7u2l4XaCz+lzgOfMfhMonXr7TWzeZb98dbgIzBT8Ub8eYYUqfZ4rVJ/MDbIDgPqTulJ/xvntWAtjIisqnwxOkGz0n077FARoY79GdA6HPE4rOy196NiMWHTZlSSApcOgXpy/fHV2joaNKu3ffsAnRcBf4K/6NcIG6tIxk3HyoXPjASqfUgXbYN5PzpL2njkR9QMjeDTVHDTCgRuxOegjoO0FvKzP/t/gmVdI24+G7NIe8JX6Wv3dDyldMA+4YB5wwTygtd+dwRqaTqrLb1l73zTSN52CNpnHuQOYPsDblybgxfkXh/oVtr+N1DEBJdhRJyd/Bd/q1z+cbNrD17iVKyajcnv9arhOkRPgsruuD6DmNPwpDNrLw2CoTgHni4yALr0L29+tiKAEIPn868ejx//8rpWP3OEOl5On9OwpcQm0MhafP/ey8f1uvDNIgGLQG8z4YO99ENgg95etwv4uYJYY8fUGHYH6j6fscHFZMftlAl9i+9XL73X3N/n+ZStOzfVfRvYXhrbdKOpEgVQTg/wsDuDD3kwOfQNMTJ5y+/ltUDWLunyxnRF46IqlBzGMY4X7inggREFioIyMjIyMHWCIB6ZNKAcXseo3vLTQTkVE7348dlwJJSz0+wLfmi8BhZqfw3D4ww/wHVLnEd5/fgYvXsDZ3MlsvYUbbnDjDZ3MN3TJG4+bxjAaDl8TBri9qxEw1ccao2wTNAMLHo2f+sjrXwb/9qHoYqgPMBXJTVfOpmrZH23y6uvo0LHSyY6fHGwKfHJlAuMFvObjDYrIqxBgQi20h7Hd/nYVLmno+eaNUm/eeH2GCuopntnhBJAlI2AHo9CCh1I1QxUdAbqqGY9BBLwyc3W4wYVhvY8A4BoIc1l5M7vnPWphZW9/Ses3n37y9a0uGqFwFQZsQQbd386DogpgEk+dzynsAZMJXq8+ns9NeukJ0PYrNATGGefJQlhkLo7DTXr+y3bNiOsDvrXTz/C2q1DXZH84iRNwrP88Nj+u2DjYEE6RBxD9Knj16ujVHC67A7422o02RwD3gB+t7EblWvu9geOFxSnd3ROmT+nJyQkhoPlsxVONc/3TEdBos+jtA+ZzcwHgTvD1cDjaYCcItA8w9i88A8b+mqSjc6Pvqd998QguEQPmQMeo23ODN86+p0/bn1buBkT6+oBhNZ/PYY4ZAHYb3PRd4LkZmPX68NRtMZn4ASvdA+qf0jMA5MP9eeg28Nug9QiLnj5A33U1MAES6xHAUNpz/9zFAYE1gqQDMT3G6xI9pwdw/aIgKoHCS1YGlRnSq9yCjdXjgN3j+N27YyROHxmuNAeNKPpYuXIyIyMjYy0M8eros59MF/PT2c602T7eA7zvhJ9dr/vzDjXaLp4Yc5+0wllzxzHv3gdmMMM7/CcQzKgVBqYTmFn+Z+mKm8J7k0A5F/jgCfjQ1WBhQyiOqD0lYuqBb+AyzMw9Ha2G3m6c8qQx+AlqnIceQp+Sb6i9UyQWbhr54+AjnZ0VzW2TAN0DmBT6PWmc6jDBE2PK2u+nF43dyP7Q0t1pOcX2fdRvH0mF2Q4JqN35rnHjVIeaXfIAVyUuw/aHCCiJy9iF5l1621zweI8KZrPZ9iJdb7DXJ3US0OSrtZ10imt7wHY7QesAzUMz1oZ3noB3qFJ/H18j97FYuw8QDN4oeKf30osvcSW2ExLo+VcbuAuo/sUIm8fMG9xocO3Ea19J9gFYivnHJ2KnyfovZlgW3v6ySx32abQiIyMjIyPjhlFDTLxpwIgFMnTp6A3g4IDKNY+stkwAMAoIAbasxBXqUWneSAWTMjt50lTqT29rFjvXohjsDNm2YPXDFlICmrJOZ3t6tHm8AiEAl0sCeLIIorIRt+cFbew/QRsoAXb4o1XSfoywzm0FTMAoYBNvLyFu8v8HpLBtD1iKgC17wHb7AI6d9wFbvguAIGTHd4E9wG7jgIyMjIyM+434c2R3HeV/Ffx6jtZu6ijl8h59T655jhR+rdHzDOP6beABCheb8O8/WFXeOyzgf5oAhVYnKxP7CwaAf1afJu8bSrhS6tdaXeGnrRenOqOlz9d6QwYnA/3TLd+GE7qe3chA5YF5DfY0vK3adfOX/gyNp2BW25MHdxAB9qvRiiP3/XpQQFGYDU4+Mi///XumXG8pjvaUAOsBGlf4jJt+YYEzeEzAdw06F19R3juM7D1wita86GR0CKfDHgLuXCc4Bri6vMLdfjMc4VNSUNsdodo2xu/1+Xl/K5+az8jIyMhYG/z5gJTMF1GtKq/a3rpyCvz5gJTMl9GtKq/a3rpyCmfQ4WwZmS+kXFVetb115ST48wEf/AGcfG1iw+tWbpbS2vJ3nQxcVr3lH3z5h972FUTLzYpOVk7l5hD+eYcYwDcAnewOotrZ4OtrPDucqi/LRX0/RR4qx7Nn4U8g+qjffvuN6Gf+nC85vwauHjaYyubqvWYKY4VEfSUMitdnBCT1Ue63R5439m+OgCn6DroAAaHPVQxKth/wkJgHmG8bmQMsT0D6EjDfvhVRKO3ywOQUgRA7nmL1uawZmHf1k+DPBwQ6NdcJ+k6Md1LA5f5ONdhJ8vZ5J0vLHT99srkGOjmJbd/G1r2Nriqnse1AZt1AalU5jW2HsuuG0qvKGRkZGRkZGRG0gcONyXsP9v8D0/IdJADiBNiXl3327WRGgOL/9HC/0XwlIURkRhC4tz6Z/fu7fUf2gHvfB9z3u0BGRkZGRkbGplHcnkgguQoSqtUXuhbs/wPtMwqV0HUJAvj5vk32b8IDuL23yn7qAXZ5u32hbRX7d3o82Df1FZXvbh9QOfhyxldr/+3xgXU9oKmvsHyr7F/XA269/eveBXrsv7N9QALe/tvjA0kPWAXGbvebkbHn+D/J5nMcHzx1UAAAAABJRU5ErkJggg==);\
 }\
 .ui-widget-header .ui-icon,\
 .ui-state-default .ui-icon,\
 .ui-state-hover .ui-icon,\
 .ui-state-focus .ui-icon,\
 .ui-state-active .ui-icon,\
 .ui-state-highlight .ui-icon,\
 .ui-state-error .ui-icon,\
 .ui-state-error-text .ui-icon\
 {\
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAADwCAMAAADYSUr5AAAA7VBMVEX8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vz8/vywC3+8AAAATnRSTlMAGBAyBAhQv4OZLiJUcEBmYBoSzQwgPBZCSEoeWiYwUiyFNIeBw2rJz8c4RBy9uXyrtaWNqa2zKP2fJO8KBgKPo2KVoa9s351GPm5+kWho0kj9AAAPhUlEQVR4nO1djWLbthEGyUiq5YSSLXtp7FpLOmfzkmxr126tmi2p03RJ1/Xe/3EGgARxPyAgRbIk2/hkSz4CJO4+HsE7AJSVysjI2AMUUOxahZ2iANhzBtZWr4BoIRSYAVN5u4QwDwQDRbcwfUi5KS3wFuDmFnQLa4Dtb//cqktwD5QEFFwfUs7PoCCA7y4bEJVFizcIob8KmhAplwwqVjt+9FBl3uINQniwEiryEyw9JHqGpQdEFNi+B4QQ7QOiHhysIPoAxUqxvdvvA9K42bsAv4S2fxfYOe57IJSRkZGRkZGxx7jxSHDHcRBXQMTyIjInBgHwBJ/bEx8PEANC+uhbpSSggCBAVODVabpI1S/k4WLZpTn6NpMhoX9Y40hxYERFpMcqUs4AloCtDQdID1YhnyXZ2hLjAYWiO9Dy1PDB7tPhIqLx+uMB8grZaR+Qxl2/C2RkZGRkZGRk7A7rBf7J0DR5/LUTjzUPIPSPGvQJiVJiB7kcQCiUOJrcFNtDZIf2xarQ3aGvLNxAVIFAabz90BFiBIlycTBhgWwOWCH0FLYHlPqwHaCvcIn2ZbosCevfPTRiFFcgvHukCjWwrc3GrGh1fsAof8EaUReKXkCB4/MzFNo97qLpFiKFYv/kNR5YQxQbQEofkZ2OuEOHqqT6gFTpru8CN7x/+jaZkZGRkZGRcV+x/rLUNcMMqUAscgnFocmpqkTzqymwVAPxfJ5PnIUUQOUKT04tEdWZyv3JCQSn96WS4pD97QfyW25A7NhSAbyhmVj0FEltA4vdiygBibXhoUYgykCUP7HwPTDeEqAIcHVMkZg7Zx4k0uFANs63hPQXCoRLAwdgGsr9Az7Qv7sgQGgg1aPl/BJLExBWgG4RFRLFImGmIquPC/klEGyCG0AuAXaJJC+B8FVe9NYQDEcXB8g6AQcjYJ1goJIggHWCrFR0S6kRHN5+4BzFi8NaoN35NRxUvL+JJdZr7PV4wK6fj8nIyMjIyNhr3OxdXAYq7FHZwB6bDSzSh4sF0utChqo0NAvaT1hLzXwFinmCzmeDucEQK18TTaQoFgP7bNC+RZ4OT4T6gQogDFYk+1QxQlj19QGSAWKiLYp8P0Ag1Gbz1ULfWHLg9iUnQNK5QQJcukm04blKLH2GgEJCY+HzXAZWCvHKco3Bp6MIaCjSXXRJyOxeqhnzEaF93MfFGW/O16ZvDL5TM4MJIjujz/cHypkQuuzRwWJ93BKdIt+wCRAPl9kpe2Ikkb2mFgGlxh/i40d3EHfdvoyMjIyMu43ylt/IAmGHnN5iIt7wKfbv01RAcJqFRl9lcjYQSnbQqKgC4fYOwSJt6N6trE0twZ9kN/PqNpTQeICvr4TLsDYC06U7BMjshS+v1/aT7IwQYD5LcgRQXMT2FrBfBLjZ6151jDElk9tPFfpUgk2yregusX25BJbwAFEfM+YI6vGAti4bTtizB+TjfQCrERyhKb2X8D6A9wX75P4t4neBYJeP6pdhg/gQl8MWvytzeSTjgOQBynQdh/iXKdxOrGJ/RkZGRsb9QmXihGr5+g8GGg9uTh+KoVZuNIzV+CwRucFBEyr1mVjx4irOxwM1BhirB6Q+2eNQi4eqR+aF6mELtoMzCR7V9RAFe/ZvQogNiyY8FPSUTFsLp8TeTmMui5mtw7bcaT0Yw2AA4wFRQIlkgq+1DQrNhkmoxS5Jq+u6bMAIGRECEANgXHTgWzwgBOhDH2l0oTQ4D8D5NMktBgNywAEMjo8rwATMZrPY7JGxBoJCkIBDQiAY09EGTUiBCWkUpISfGPR5AAwBfZiG2z7Ayc1yeKTxid39xBNwfHr4O0LA48ePFTvhYrF1r4tyAoz9n2MCqEuBtp/6GDR0oAYfG/R6wJExHYZHfhygsv7fEWCOj4bYmsP5A+pL4MkTfAnMlD4F+r3bobKvTyTA2P/w7PN+Agq2QW8piqMCpTBwenoKvX0AHGkGtP2YAPvTEWA7QUTAudn7/NxtOG46wWNmDtpBEkBzN7rBEvAFHp+YTB/q97qPAN4gHFqgBi8uLsC7qPCA6mg41G/+ErByPwEXDdoNxRhOx+M5jPEzQugS0ht+b1/Y3gEnYMAIAOIBE29/hIDucE8tmMsNOgK4B1RHFu4UCRlMHzv0xzcajcfdXWDs2h8TArBCkoDUJYDLmz6w7ip3BFS0ve5wTRwAn6keMA9I3QYbfSZ0DKbyt+7OXjGI1idPcfNyAyfAMlCrzaGqphYrxHocLHRJVycnfGUcbtT+jIyMjIw9x7Nn8fJSzG0TmFtO8rZT+XT3S3ub+tKJbbLd5diTVp50+zahyeHSslJ/YPrU0fuazrZO2CZ92/ZCCVXlGRiZKPJyPPRxyIFWeXLQBXJBKiq/3divEAN6ZwM200Qjm7EJBZeWm/PRWVCbYK7s7u2l4XaCz+lzgOfMfhMonXr7TWzeZb98dbgIzBT8Ub8eYYUqfZ4rVJ/MDbIDgPqTulJ/xvntWAtjIisqnwxOkGz0n077FARoY79GdA6HPE4rOy196NiMWHTZlSSApcOgXpy/fHV2joaNKu3ffsAnRcBf4K/6NcIG6tIxk3HyoXPjASqfUgXbYN5PzpL2njkR9QMjeDTVHDTCgRuxOegjoO0FvKzP/t/gmVdI24+G7NIe8JX6Wv3dDyldMA+4YB5wwTygtd+dwRqaTqrLb1l73zTSN52CNpnHuQOYPsDblybgxfkXh/oVtr+N1DEBJdhRJyd/Bd/q1z+cbNrD17iVKyajcnv9arhOkRPgsruuD6DmNPwpDNrLw2CoTgHni4yALr0L29+tiKAEIPn868ejx//8rpWP3OEOl5On9OwpcQm0MhafP/ey8f1uvDNIgGLQG8z4YO99ENgg95etwv4uYJYY8fUGHYH6j6fscHFZMftlAl9i+9XL73X3N/n+ZStOzfVfRvYXhrbdKOpEgVQTg/wsDuDD3kwOfQNMTJ5y+/ltUDWLunyxnRF46IqlBzGMY4X7inggREFioIyMjIyMHWCIB6ZNKAcXseo3vLTQTkVE7348dlwJJSz0+wLfmi8BhZqfw3D4ww/wHVLnEd5/fgYvXsDZ3MlsvYUbbnDjDZ3MN3TJG4+bxjAaDl8TBri9qxEw1ccao2wTNAMLHo2f+sjrXwb/9qHoYqgPMBXJTVfOpmrZH23y6uvo0LHSyY6fHGwKfHJlAuMFvObjDYrIqxBgQi20h7Hd/nYVLmno+eaNUm/eeH2GCuopntnhBJAlI2AHo9CCh1I1QxUdAbqqGY9BBLwyc3W4wYVhvY8A4BoIc1l5M7vnPWphZW9/Ses3n37y9a0uGqFwFQZsQQbd386DogpgEk+dzynsAZMJXq8+ns9NeukJ0PYrNATGGefJQlhkLo7DTXr+y3bNiOsDvrXTz/C2q1DXZH84iRNwrP88Nj+u2DjYEE6RBxD9Knj16ujVHC67A7422o02RwD3gB+t7EblWvu9geOFxSnd3ROmT+nJyQkhoPlsxVONc/3TEdBos+jtA+ZzcwHgTvD1cDjaYCcItA8w9i88A8b+mqSjc6Pvqd998QguEQPmQMeo23ODN86+p0/bn1buBkT6+oBhNZ/PYY4ZAHYb3PRd4LkZmPX68NRtMZn4ASvdA+qf0jMA5MP9eeg28Nug9QiLnj5A33U1MAES6xHAUNpz/9zFAYE1gqQDMT3G6xI9pwdw/aIgKoHCS1YGlRnSq9yCjdXjgN3j+N27YyROHxmuNAeNKPpYuXIyIyMjYy0M8eros59MF/PT2c602T7eA7zvhJ9dr/vzDjXaLp4Yc5+0wllzxzHv3gdmMMM7/CcQzKgVBqYTmFn+Z+mKm8J7k0A5F/jgCfjQ1WBhQyiOqD0lYuqBb+AyzMw9Ha2G3m6c8qQx+AlqnIceQp+Sb6i9UyQWbhr54+AjnZ0VzW2TAN0DmBT6PWmc6jDBE2PK2u+nF43dyP7Q0t1pOcX2fdRvH0mF2Q4JqN35rnHjVIeaXfIAVyUuw/aHCCiJy9iF5l1621zweI8KZrPZ9iJdb7DXJ3US0OSrtZ10imt7wHY7QesAzUMz1oZ3noB3qFJ/H18j97FYuw8QDN4oeKf30osvcSW2ExLo+VcbuAuo/sUIm8fMG9xocO3Ea19J9gFYivnHJ2KnyfovZlgW3v6ySx32abQiIyMjIyPjhlFDTLxpwIgFMnTp6A3g4IDKNY+stkwAMAoIAbasxBXqUWneSAWTMjt50lTqT29rFjvXohjsDNm2YPXDFlICmrJOZ3t6tHm8AiEAl0sCeLIIorIRt+cFbew/QRsoAXb4o1XSfoywzm0FTMAoYBNvLyFu8v8HpLBtD1iKgC17wHb7AI6d9wFbvguAIGTHd4E9wG7jgIyMjIyM+434c2R3HeV/Ffx6jtZu6ijl8h59T655jhR+rdHzDOP6beABCheb8O8/WFXeOyzgf5oAhVYnKxP7CwaAf1afJu8bSrhS6tdaXeGnrRenOqOlz9d6QwYnA/3TLd+GE7qe3chA5YF5DfY0vK3adfOX/gyNp2BW25MHdxAB9qvRiiP3/XpQQFGYDU4+Mi///XumXG8pjvaUAOsBGlf4jJt+YYEzeEzAdw06F19R3juM7D1wita86GR0CKfDHgLuXCc4Bri6vMLdfjMc4VNSUNsdodo2xu/1+Xl/K5+az8jIyMhYG/z5gJTMF1GtKq/a3rpyCvz5gJTMl9GtKq/a3rpyCmfQ4WwZmS+kXFVetb115ST48wEf/AGcfG1iw+tWbpbS2vJ3nQxcVr3lH3z5h972FUTLzYpOVk7l5hD+eYcYwDcAnewOotrZ4OtrPDucqi/LRX0/RR4qx7Nn4U8g+qjffvuN6Gf+nC85vwauHjaYyubqvWYKY4VEfSUMitdnBCT1Ue63R5439m+OgCn6DroAAaHPVQxKth/wkJgHmG8bmQMsT0D6EjDfvhVRKO3ywOQUgRA7nmL1uawZmHf1k+DPBwQ6NdcJ+k6Md1LA5f5ONdhJ8vZ5J0vLHT99srkGOjmJbd/G1r2Nriqnse1AZt1AalU5jW2HsuuG0qvKGRkZGRkZGRG0gcONyXsP9v8D0/IdJADiBNiXl3327WRGgOL/9HC/0XwlIURkRhC4tz6Z/fu7fUf2gHvfB9z3u0BGRkZGRkbGplHcnkgguQoSqtUXuhbs/wPtMwqV0HUJAvj5vk32b8IDuL23yn7qAXZ5u32hbRX7d3o82Df1FZXvbh9QOfhyxldr/+3xgXU9oKmvsHyr7F/XA269/eveBXrsv7N9QALe/tvjA0kPWAXGbvebkbHn+D/J5nMcHzx1UAAAAABJRU5ErkJggg==);\
 }\
 /* positioning */\
 .ui-icon-alert { background-position: 0 -144px; }\
 .ui-icon-arrow-1-e { background-position: -32px -32px; }\
 .ui-icon-arrow-1-n { background-position: 0 -32px; }\
 .ui-icon-arrow-1-ne { background-position: -16px -32px; }\
 .ui-icon-arrow-1-nw { background-position: -112px -32px; }\
 .ui-icon-arrow-1-s { background-position: -64px -32px; }\
 .ui-icon-arrow-1-se { background-position: -48px -32px; }\
 .ui-icon-arrow-1-sw { background-position: -80px -32px; }\
 .ui-icon-arrow-1-w { background-position: -96px -32px; }\
 .ui-icon-arrow-2-e-w { background-position: -160px -32px; }\
 .ui-icon-arrow-2-n-s { background-position: -128px -32px; }\
 .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }\
 .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }\
 .ui-icon-arrow-4 { background-position: 0 -80px; }\
 .ui-icon-arrow-4-diag { background-position: -16px -80px; }\
 .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }\
 .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }\
 .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }\
 .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }\
 .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }\
 .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }\
 .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }\
 .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }\
 .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }\
 .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }\
 .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }\
 .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }\
 .ui-icon-arrowstop-1-e { background-position: -208px -32px; }\
 .ui-icon-arrowstop-1-n { background-position: -192px -32px; }\
 .ui-icon-arrowstop-1-s { background-position: -224px -32px; }\
 .ui-icon-arrowstop-1-w { background-position: -240px -32px; }\
 .ui-icon-arrowthick-1-e { background-position: -32px -48px; }\
 .ui-icon-arrowthick-1-n { background-position: 0 -48px; }\
 .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }\
 .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }\
 .ui-icon-arrowthick-1-s { background-position: -64px -48px; }\
 .ui-icon-arrowthick-1-se { background-position: -48px -48px; }\
 .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }\
 .ui-icon-arrowthick-1-w { background-position: -96px -48px; }\
 .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }\
 .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }\
 .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }\
 .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }\
 .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }\
 .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }\
 .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }\
 .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }\
 .ui-icon-battery-0 { background-position: -48px -176px; }\
 .ui-icon-battery-1 { background-position: -64px -176px; }\
 .ui-icon-battery-2 { background-position: -80px -176px; }\
 .ui-icon-battery-3 { background-position: -96px -176px; }\
 .ui-icon-bookmark { background-position: -224px -96px; }\
 .ui-icon-bullet { background-position: -80px -144px; }\
 .ui-icon-calculator { background-position: -112px -112px; }\
 .ui-icon-calendar { background-position: -32px -112px; }\
 .ui-icon-cancel { background-position: 0 -128px; }\
 .ui-icon-carat-1-e { background-position: -32px 0; }\
 .ui-icon-carat-1-n { background-position: 0 0; }\
 .ui-icon-carat-1-ne { background-position: -16px 0; }\
 .ui-icon-carat-1-nw { background-position: -112px 0; }\
 .ui-icon-carat-1-s { background-position: -64px 0; }\
 .ui-icon-carat-1-se { background-position: -48px 0; }\
 .ui-icon-carat-1-sw { background-position: -80px 0; }\
 .ui-icon-carat-1-w { background-position: -96px 0; }\
 .ui-icon-carat-2-e-w { background-position: -144px 0; }\
 .ui-icon-carat-2-n-s { background-position: -128px 0; }\
 .ui-icon-cart { background-position: -48px -112px; }\
 .ui-icon-check { background-position: -64px -144px; }\
 .ui-icon-circle-arrow-e { background-position: -112px -192px; }\
 .ui-icon-circle-arrow-n { background-position: -160px -192px; }\
 .ui-icon-circle-arrow-s { background-position: -128px -192px; }\
 .ui-icon-circle-arrow-w { background-position: -144px -192px; }\
 .ui-icon-circle-check { background-position: -208px -192px; }\
 .ui-icon-circle-close { background-position: -32px -192px; }\
 .ui-icon-circle-minus { background-position: -16px -192px; }\
 .ui-icon-circle-plus { background-position: 0 -192px; }\
 .ui-icon-circle-triangle-e { background-position: -48px -192px; }\
 .ui-icon-circle-triangle-n { background-position: -96px -192px; }\
 .ui-icon-circle-triangle-s { background-position: -64px -192px; }\
 .ui-icon-circle-triangle-w { background-position: -80px -192px; }\
 .ui-icon-circle-zoomin { background-position: -176px -192px; }\
 .ui-icon-circle-zoomout { background-position: -192px -192px; }\
 .ui-icon-circlesmall-close { background-position: -32px -208px; }\
 .ui-icon-circlesmall-minus { background-position: -16px -208px; }\
 .ui-icon-circlesmall-plus { background-position: 0 -208px; }\
 .ui-icon-clipboard { background-position: -160px -128px; }\
 .ui-icon-clock { background-position: -80px -112px; }\
 .ui-icon-close { background-position: -80px -128px; }\
 .ui-icon-closethick { background-position: -96px -128px; }\
 .ui-icon-comment { background-position: -128px -96px; }\
 .ui-icon-contact { background-position: -192px -128px; }\
 .ui-icon-copy { background-position: -176px -128px; }\
 .ui-icon-disk { background-position: -96px -112px; }\
 .ui-icon-document { background-position: -32px -96px; }\
 .ui-icon-document-b { background-position: -48px -96px; }\
 .ui-icon-eject { background-position: -112px -160px; }\
 .ui-icon-extlink { background-position: -32px -80px; }\
 .ui-icon-flag { background-position: -16px -112px; }\
 .ui-icon-folder-collapsed { background-position: 0 -96px; }\
 .ui-icon-folder-open { background-position: -16px -96px; }\
 .ui-icon-gear { background-position: -192px -112px; }\
 .ui-icon-grip-diagonal-se { background-position: -80px -224px; }\
 .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }\
 .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }\
 .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }\
 .ui-icon-grip-solid-vertical { background-position: -32px -224px; }\
 .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }\
 .ui-icon-heart { background-position: -208px -112px; }\
 .ui-icon-help { background-position: -48px -144px; }\
 .ui-icon-home { background-position: 0 -112px; }\
 .ui-icon-image { background-position: -208px -128px; }\
 .ui-icon-info { background-position: -16px -144px; }\
 .ui-icon-key { background-position: -112px -128px; }\
 .ui-icon-lightbulb { background-position: -128px -128px; }\
 .ui-icon-link { background-position: -240px -112px; }\
 .ui-icon-locked { background-position: -192px -96px; }\
 .ui-icon-mail-closed { background-position: -80px -96px; }\
 .ui-icon-mail-open { background-position: -96px -96px; }\
 .ui-icon-minus { background-position: -48px -128px; }\
 .ui-icon-minusthick { background-position: -64px -128px; }\
 .ui-icon-newwin { background-position: -48px -80px; }\
 .ui-icon-note { background-position: -64px -96px; }\
 .ui-icon-notice { background-position: -32px -144px; }\
 .ui-icon-pause { background-position: -16px -160px; }\
 .ui-icon-pencil { background-position: -64px -112px; }\
 .ui-icon-person { background-position: -144px -96px; }\
 .ui-icon-pin-s { background-position: -144px -144px; }\
 .ui-icon-pin-w { background-position: -128px -144px; }\
 .ui-icon-play { background-position: 0 -160px; }\
 .ui-icon-plus { background-position: -16px -128px; }\
 .ui-icon-plusthick { background-position: -32px -128px; }\
 .ui-icon-power { background-position: 0 -176px; }\
 .ui-icon-print { background-position: -160px -96px; }\
 .ui-icon-radio-off { background-position: -96px -144px; }\
 .ui-icon-radio-on { background-position: -112px -144px; }\
 .ui-icon-refresh { background-position: -64px -80px; }\
 .ui-icon-scissors { background-position: -144px -128px; }\
 .ui-icon-script { background-position: -240px -128px; }\
 .ui-icon-search { background-position: -160px -112px; }\
 .ui-icon-seek-end { background-position: -64px -160px; }\
 .ui-icon-seek-first { background-position: -80px -160px; }\
 .ui-icon-seek-next { background-position: -32px -160px; }\
 .ui-icon-seek-prev { background-position: -48px -160px; }\
 .ui-icon-seek-start { background-position: -80px -160px; }\
 .ui-icon-shuffle { background-position: -80px -80px; }\
 .ui-icon-signal { background-position: -32px -176px; }\
 .ui-icon-signal-diag { background-position: -16px -176px; }\
 .ui-icon-squaresmall-close { background-position: -80px -208px; }\
 .ui-icon-squaresmall-minus { background-position: -64px -208px; }\
 .ui-icon-squaresmall-plus { background-position: -48px -208px; }\
 .ui-icon-star { background-position: -224px -112px; }\
 .ui-icon-stop { background-position: -96px -160px; }\
 .ui-icon-suitcase { background-position: -112px -96px; }\
 .ui-icon-tag { background-position: -240px -96px; }\
 .ui-icon-transfer-e-w { background-position: -96px -80px; }\
 .ui-icon-transferthick-e-w { background-position: -112px -80px; }\
 .ui-icon-trash { background-position: -176px -96px; }\
 .ui-icon-triangle-1-e { background-position: -32px -16px; }\
 .ui-icon-triangle-1-n { background-position: 0 -16px; }\
 .ui-icon-triangle-1-ne { background-position: -16px -16px; }\
 .ui-icon-triangle-1-nw { background-position: -112px -16px; }\
 .ui-icon-triangle-1-s { background-position: -64px -16px; }\
 .ui-icon-triangle-1-se { background-position: -48px -16px; }\
 .ui-icon-triangle-1-sw { background-position: -80px -16px; }\
 .ui-icon-triangle-1-w { background-position: -96px -16px; }\
 .ui-icon-triangle-2-e-w { background-position: -144px -16px; }\
 .ui-icon-triangle-2-n-s { background-position: -128px -16px; }\
 .ui-icon-unlocked { background-position: -208px -96px; }\
 .ui-icon-video { background-position: -224px -128px; }\
 .ui-icon-volume-off { background-position: -128px -160px; }\
 .ui-icon-volume-on { background-position: -144px -160px; }\
 .ui-icon-wrench { background-position: -176px -112px; }\
 .ui-icon-zoomin { background-position: -128px -112px; }\
 .ui-icon-zoomout { background-position: -144px -112px; }\
 /* Corner radius */\
 .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {\
  -moz-border-radius-topleft: 4px;\
  -webkit-border-top-left-radius: 4px;\
  -khtml-border-top-left-radius: 4px;\
  border-top-left-radius: 4px;\
 }\
 .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {\
  -moz-border-radius-topright: 4px;\
  -webkit-border-top-right-radius: 4px;\
  -khtml-border-top-right-radius: 4px;\
  border-top-right-radius: 4px;\
 }\
 .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {\
  -moz-border-radius-bottomleft: 4px;\
  -webkit-border-bottom-left-radius: 4px;\
  -khtml-border-bottom-left-radius: 4px;\
  border-bottom-left-radius: 4px;\
 }\
 .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {\
  -moz-border-radius-bottomright: 4px;\
  -webkit-border-bottom-right-radius: 4px;\
  -khtml-border-bottom-right-radius: 4px;\
  border-bottom-right-radius: 4px;\
 }\
 /* Overlays */\
 .ui-widget-overlay\
 {\
  background-color: #666666;\
  opacity: .50;\
  filter:Alpha(Opacity=50);\
 }\
 .ui-widget-shadow\
 {\
  margin: -5px 0 0 -5px;\
  padding: 5px;\
  background-color: #000000;\
  opacity: .20;\
  filter:Alpha(Opacity=20);\
  -moz-border-radius: 5px;\
  -khtml-border-radius: 5px;\
  -webkit-border-radius: 5px;\
  border-radius: 5px;\
 }\
 /* jQuery UI Resizable 1.8.16 */\
 .ui-resizable\
 {\
  position: relative;\
 }\
 .ui-resizable-handle\
 {\
  position: absolute;\
  font-size: 0.1px;\
  z-index: 99999;\
  display: block;\
 }\
 .ui-resizable-disabled .ui-resizable-handle,\
 .ui-resizable-autohide .ui-resizable-handle\
 {\
  display: none;\
 }\
 .ui-resizable-n\
 {\
  cursor: n-resize;\
  height: 7px;\
  width: 100%;\
  top: -5px;\
  left: 0;\
 }\
 .ui-resizable-s\
 {\
  cursor: s-resize;\
  height: 7px;\
  width: 100%;\
  bottom: -5px;\
  left: 0;\
 }\
 .ui-resizable-e\
 {\
  cursor: e-resize;\
  width: 7px;\
  right: -5px;\
  top: 0;\
  height: 100%;\
 }\
 .ui-resizable-w\
 {\
  cursor: w-resize;\
  width: 7px;\
  left: -5px;\
  top: 0;\
  height: 100%;\
 }\
 .ui-resizable-se\
 {\
  cursor: se-resize;\
  width: 12px;\
  height: 12px;\
  right: 1px;\
  bottom: 1px;\
 }\
 .ui-resizable-sw\
 {\
  cursor: sw-resize;\
  width: 9px;\
  height: 9px;\
  left: -5px;\
  bottom: -5px;\
 }\
 .ui-resizable-nw\
 {\
  cursor: nw-resize;\
  width: 9px;\
  height: 9px;\
  left: -5px;\
  top: -5px;\
 }\
 .ui-resizable-ne\
 {\
  cursor: ne-resize;\
  width: 9px;\
  height: 9px;\
  right: -5px;\
  top: -5px;\
 }\
 /* jQuery UI Selectable 1.8.16 */\
 .ui-selectable-helper\
 {\
  position: absolute;\
  z-index: 100;\
  border:1px dotted black;\
 }\
 /* jQuery UI Accordion 1.8.16 */\
 .ui-accordion\
 {\
  width: 100%;\
 }\
 .ui-accordion .ui-accordion-header\
 {\
  cursor: pointer;\
  position: relative;\
  margin-top: 1px;\
  zoom: 1;\
 }\
 .ui-accordion .ui-accordion-li-fix {\
  display: inline;\
 }\
 .ui-accordion .ui-accordion-header-active\
 {\
  border-bottom: 0 !important;\
 }\
 .ui-accordion .ui-accordion-header a\
 {\
  display: block;\
  font-size: 0.9em;\
  padding: .5em .5em .5em .7em;\
 }\
 .ui-accordion-icons .ui-accordion-header a\
 {\
  padding-left: 2.2em;\
 }\
 .ui-accordion .ui-accordion-header .ui-icon\
 {\
  position: absolute;\
  left: .5em;\
  top: 50%;\
  margin-top: -8px;\
 }\
 .ui-accordion .ui-accordion-content\
 {\
  padding: 1em 2.2em;\
  border-top: 0;\
  margin-top: -2px;\
  position: relative;\
  top: 1px;\
  margin-bottom: 2px;\
  overflow: auto;\
  display: none;\
  zoom: 1;\
 }\
 .ui-accordion .ui-accordion-content-active\
 {\
  display: block;\
 }\
 /* jQuery UI Autocomplete 1.8.16 */\
 .ui-autocomplete\
 {\
  position: absolute;\
  cursor: default;\
 }\
 /* jQuery UI Menu 1.8.16 */\
 .ui-menu\
 {\
  list-style:none;\
  padding: 2px;\
  margin: 0;\
  display:block;\
  float: left;\
 }\
 .ui-menu .ui-menu\
 {\
  margin-top: -3px;\
 }\
 .ui-menu .ui-menu-item\
 {\
  margin:0;\
  padding: 0;\
  zoom: 1;\
  float: left;\
  clear: left;\
  width: 100%;\
 }\
 .ui-menu .ui-menu-item a\
 {\
  text-decoration:none;\
  display:block;\
  padding:.2em .4em;\
  line-height:1.5;\
  zoom:1;\
 }\
 .ui-menu .ui-menu-item a.ui-state-hover,\
 .ui-menu .ui-menu-item a.ui-state-active\
 {\
  font-weight: normal;\
  margin: -1px;\
 }\
 /* jQuery UI Button 1.8.16 */\
 .ui-button\
 {\
  display: inline-block;\
  position: relative;\
  padding: 0;\
  margin-right: .1em;\
  text-decoration: none !important;\
  cursor: pointer;\
  text-align: center;\
  zoom: 1;\
  overflow: visible;\
 }\
 /* the overflow property removes extra width in IE */\
 .ui-button-icon-only\
 {\
  width: 2.2em;\
 }\
 /* to make room for the icon, a width needs to be set here */\
 button.ui-button-icon-only\
 {\
  width: 2.4em;\
 }\
 /* button elements seem to need a little more width */\
 .ui-button-icons-only\
 {\
  width: 3.4em;\
 }\
 button.ui-button-icons-only\
 {\
  width: 3.7em;\
 }\
 /*button text element */\
 .ui-button .ui-button-text\
 {\
  display: block;\
  line-height: 1.4;\
 }\
 .ui-button-text-only .ui-button-text\
 {\
  padding: .4em 1em;\
 }\
 .ui-button-icon-only .ui-button-text,\
 .ui-button-icons-only .ui-button-text\
 {\
  padding: .4em;\
  text-indent: -9999999px;\
 }\
 .ui-button-text-icon-primary .ui-button-text,\
 .ui-button-text-icons .ui-button-text\
 {\
  padding: .4em 1em .4em 2.1em;\
 }\
 .ui-button-text-icon-secondary .ui-button-text,\
 .ui-button-text-icons .ui-button-text\
 {\
  padding: .4em 2.1em .4em 1em;\
 }\
 .ui-button-text-icons .ui-button-text\
 {\
  padding-left: 2.1em;\
  padding-right: 2.1em;\
 }\
 /* no icon support for input elements, provide padding by default */\
 input.ui-button\
 {\
  padding: .4em 1em;\
 }\
 /*button icon element(s) */\
 .ui-button-icon-only .ui-icon,\
 .ui-button-text-icon-primary .ui-icon,\
 .ui-button-text-icon-secondary .ui-icon,\
 .ui-button-text-icons .ui-icon,\
 .ui-button-icons-only .ui-icon\
 {\
  position: absolute;\
  top: 50%;\
  margin-top: -8px;\
 }\
 .ui-button-icon-only .ui-icon {\
  left: 50%;\
  margin-left: -8px;\
 }\
 .ui-button-text-icon-primary .ui-button-icon-primary,\
 .ui-button-text-icons .ui-button-icon-primary,\
 .ui-button-icons-only .ui-button-icon-primary\
 {\
  left: .5em;\
 }\
 .ui-button-text-icon-secondary .ui-button-icon-secondary,\
 .ui-button-text-icons .ui-button-icon-secondary,\
 .ui-button-icons-only .ui-button-icon-secondary\
 {\
  right: .5em;\
 }\
 .ui-button-text-icons .ui-button-icon-secondary,\
 .ui-button-icons-only .ui-button-icon-secondary\
 {\
  right: .5em;\
 }\
 /*button sets*/\
 .ui-buttonset\
 {\
  margin-right: 7px;\
 }\
 .ui-buttonset .ui-button\
 {\
  margin-left: 0;\
  margin-right: -.3em;\
 }\
 /* workarounds */\
 /* reset extra padding in Firefox */\
 button.ui-button::-moz-focus-inner\
 {\
  border: 0;\
  padding: 0;\
 }\
 /* jQuery UI Dialog 1.8.16  */\
 .ui-dialog {\
  position : absolute;\
  padding  : .2em;\
  width  : 300px;\
  overflow : hidden;\
  -webkit-box-shadow : rgba(0,0,0,0.8) 0 0 10px;\
  -moz-box-shadow  : rgba(0,0,0,0.8) 0 0 10px;\
  -khtml-box-shadow : rgba(0,0,0,0.8) 0 0 10px;\
  box-shadow   : rgba(0,0,0,0.8) 0 0 10px;\
 }\
 .ui-dialog .ui-dialog-titlebar {\
  padding  : .4em 1em;\
  position : relative;\
 }\
 .ui-dialog .ui-dialog-title {\
  float    : left;\
  margin    : .1em 16px .1em 0;\
  color    : rgba(255,255,255,0.8);\
  font-weight   : bold;\
  text-shadow   : -1px -1px rgba(110,110,110,0.7);\
  -moz-text-shadow : -1px -1px rgba(110,110,110,0.7);\
  -webkit-text-shadow : -1px -1px rgba(110,110,110,0.7);\
  -khtml-text-shadow : -1px -1px rgba(110,110,110,0.7);\
 } \
 .ui-dialog .ui-dialog-titlebar-close {\
  position : absolute;\
  right  : .3em;\
  top   : 50%;\
  width  : 17px;\
  margin  : -10px 0 0 0; \
  padding  : 1px;\
  height  : 16px;\
  background-color : rgba(0,0,0,0.3);\
  border  : 1px solid transparent;\
 }\
 .ui-dialog .ui-dialog-titlebar-close span {\
  display  : block;\
 }\
 .ui-dialog .ui-dialog-titlebar-close:hover,\
 .ui-dialog .ui-dialog-titlebar-close:focus\
 {\
  background-color  : #CC334D;\
  border    : 1px solid #960D16;\
  -webkit-box-shadow : rgba(250,90,120,0.8) 0 0 8px;\
  -moz-box-shadow  : rgba(250,90,120,0.8) 0 0 8px;\
  -khtml-box-shadow : rgba(250,90,120,0.8) 0 0 8px;\
  box-shadow   : rgba(250,90,120,0.8) 0 0 8px;\
 }\
 .ui-dialog .ui-dialog-content {\
  position : relative;\
  border  : 0;\
  padding  : .5em 1em;\
  background : none;\
  overflow : auto;\
  zoom  : 1;\
 }\
 .ui-dialog .ui-dialog-buttonpane {\
  text-align : left;\
  border-width: 1px 0 0 0;\
  margin  : .5em 0 0 0;\
  padding  : .3em 1em .5em .4em;\
  background-image: none;\
 }\
 .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset\
 {\
  float  : right;\
 }\
 .ui-dialog .ui-dialog-buttonpane button {\
  margin  : .5em .4em .5em 0;\
  cursor  : pointer;\
 }\
 .ui-dialog .ui-resizable-se {\
  width  : 14px;\
  height  : 14px;\
  right  : 3px;\
  bottom  : 3px;\
 }\
 .ui-draggable .ui-dialog-titlebar\
 {\
  cursor  : move;\
 }\
 /* jQuery UI Slider 1.8.16 */\
 .ui-slider\
 {\
  position: relative;\
  text-align: left;\
 }\
 .ui-slider .ui-slider-handle\
 {\
  position: absolute;\
  z-index: 2;\
  width: 1.2em;\
  height: 1.2em;\
  cursor: default;\
 }\
 .ui-slider .ui-slider-range\
 {\
  position: absolute;\
  z-index: 1;\
  font-size: .7em;\
  display: block;\
  border: 0;\
  background-position: 0 0;\
 }\
 .ui-slider-horizontal\
 {\
  height: .8em;\
 }\
 .ui-slider-horizontal .ui-slider-handle\
 {\
  top: -.3em;\
  margin-left: -.6em;\
 }\
 .ui-slider-horizontal .ui-slider-range\
 {\
  top: 0;\
  height: 100%;\
 }\
 .ui-slider-horizontal .ui-slider-range-min\
 {\
  left: 0;\
 }\
 .ui-slider-horizontal .ui-slider-range-max\
 {\
  right: 0;\
 }\
 .ui-slider-vertical\
 {\
  width: .8em;\
  height: 100px;\
 }\
 .ui-slider-vertical .ui-slider-handle\
 {\
  left: -.3em;\
  margin-left: 0;\
  margin-bottom: -.6em;\
 }\
 .ui-slider-vertical .ui-slider-range\
 {\
  left: 0;\
  width: 100%;\
 }\
 .ui-slider-vertical .ui-slider-range-min\
 {\
  bottom: 0;\
 }\
 .ui-slider-vertical .ui-slider-range-max\
 {\
  top: 0;\
 }\
 /* jQuery UI Tabs 1.8.16 */\
 .ui-tabs\
 {\
  position: relative;\
  padding: .2em;\
  zoom: 1;\
 }\
 /* position: relative prevents IE scroll bug\
    (element with position: relative inside container with overflow: auto appear as "fixed") */\
 .ui-tabs .ui-tabs-nav\
 {\
  margin: 0;\
  padding: .2em .2em 0;\
 }\
 .ui-tabs .ui-tabs-nav li\
 {\
  list-style: none;\
  float: left;\
  position: relative;\
  top: 1px;\
  margin: 0 .2em 1px 0;\
  border-bottom: 0 !important;\
  padding: 0;\
  white-space: nowrap;\
 }\
 .ui-tabs .ui-tabs-nav li a\
 {\
  float: left;\
  padding: .5em 1em;\
  text-decoration: none;\
 }\
 .ui-tabs .ui-tabs-nav li.ui-tabs-selected\
 {\
  margin-bottom: 0;\
  padding-bottom: 1px;\
 }\
 .ui-tabs .ui-tabs-nav li.ui-tabs-selected a,\
 .ui-tabs .ui-tabs-nav li.ui-state-disabled a,\
 .ui-tabs .ui-tabs-nav li.ui-state-processing a\
 {\
  cursor: text;\
 }\
 .ui-tabs .ui-tabs-nav li a,\
 .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a\
 {\
  cursor: pointer;\
 }\
 /* first selector in group seems obsolete, but required to overcome bug in Opera\
    applying cursor: text overall if defined elsewhere... */\
 .ui-tabs .ui-tabs-panel\
 {\
  display: block;\
  border-width: 0;\
  padding: 1em 1.4em;\
  background: none;\
 }\
 .ui-tabs .ui-tabs-hide\
 {\
  display: none !important;\
 }\
 /* jQuery UI Progressbar 1.8.16 */\
 .ui-progressbar\
 {\
  height:2em;\
  text-align: left;\
 }\
 .ui-progressbar .ui-progressbar-value\
 {\
  margin: -1px;\
  height:100%;\
 }\
 /* 3D Fx */\
 .ui-widget-content,\
 .ui-widget-header,\
 .ui-state-default,\
 .ui-widget-header .ui-state-focus,\
 .ui-widget-header .ui-state-active,\
 .ui-widget-header .ui-state-highlight,\
 .ui-dialog .ui-dialog-titlebar-close\
 {\
  background-image : linear-gradient(bottom, rgba(255,255,255,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.7) 99%);\
  background-image : -moz-linear-gradient(bottom, rgba(255,255,255,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.7) 99%);\
  background-image : -webkit-linear-gradient(bottom, rgba(255,255,255,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.7) 99%);\
  background-image : -khtml-linear-gradient(bottom, rgba(255,255,255,0.1) 10%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0.7) 99%);\
 }\
 ').appendTo("head");


/********************************************************************************
 * Extract the flashvars from the SWF object and initialise the appropriate     *
 * global variables.                                                            *
 *                                                                              *
 * USED                                                                         *
 *  - api_server                                                                *
 *  - dragon_heart                                                              *
 *  - facebook_id                                                               *
 *  - locale                                                                    *
 *  - session_id                                                                *
 *  - user_hash                                                                 *
 *  - user_id                                                                   *
 *  - user_time                                                                 *
 *                                                                              *
 * UNUSED                                                                       *
 *  - building_cachebreaker                                                     *
 *  - lazy_loaded_swf_cachebreaker                                              *
 *  - primary_ui_cachebreaker                                                   *
 *  - pub_port                                                                  *
 *  - pub_server                                                                *
 *  - second_ui_cachebreaker                                                    *
 *  - sound_cachebreaker                                                        *
 *  - s3_server                                                                 *
 *  - s3_swf_prefix                                                             *
 ********************************************************************************/
 var API_SERVER, /* Global constants from object flashvars (see getFlashvars) */
  DRAGON_HEART,
  FACEBOOK_ID,
  LOCALE,
  SESSION_ID,
  USER_HASH,
  USER_ID,
  USER_TIME;

function getFlashvars() {
 var flashvars = $J(SWF_OBJECT + " param[name='flashvars']").attr("value").split("&"),
  keyValue,
  rslt = {};
  
 $J.each(flashvars, function () {
  keyValue = this.split("=");
  rslt[keyValue[0]] = keyValue[1];
 });
 
 API_SERVER  = rslt.api_server;
 DRAGON_HEART = rslt.dragon_heart;
 FACEBOOK_ID  = rslt.facebook_id;
 LOCALE   = rslt.locale;
 SESSION_ID  = rslt.session_id;
 USER_HASH  = rslt.user_hash;
 USER_ID   = rslt.user_id;
 USER_TIME  = rslt.user_time;
 SERVER_ID  = ( /^realm(\d+)\./.exec(API_SERVER) || ['',''] )[1];
}

getFlashvars();




/************************
**   scriptStartUp
*************************/

console.log(SCRIPT_NAME + ' Startup in : ' + timeFormat(SCRIPT_STARTUP_DELAY/1000));

function scriptStartUp() {

 try {
  
  WinLog.enabled = ENABLE_WINLOG;
  
  
  /**  Data Initialization
  ***************************************/
  Data.init({ data_version : '' });

  // Verify that the version of the script data structure is the same as the version stored
  if ( Data.data_version == undefined || Data.data_version != DATA_VERSION ) {
   // Clears all stored data structure because the data version is different
   Data.clearStorage();
   Data.data_version = DATA_VERSION;
  }
  
  // Init Defaults Options
  Data.init({
   options  : {
    mainBox   : { draggable:true, x:0, y:0 },
    objAttack  : { enabled:false, repeatTime:3660, delayMin:30, delayMax:90, levelEnable:[], levelDist:[null,10,10,10,10,10,10,10,10,10,10,10], deleteObjAttacks:false, stopAttackOnLoss:false, recallEncamped:false, logAttacks:true, abadonWildernesses:false, maxMarches:10, troops:[], clearAllTargets:false },
    currentTab  : false,
    attackTab  : 0,
    mapTab   : 0,
    jobsTab   : 0,
    autoBuild  : { enabled:false, buildingEnable:[{},{},{},{},{}], buildCap:[{},{},{},{},{}] },
    autoResearch : { enabled:false, researchEnable:[], researchCap:[] },
    autoTrain  : { enabled:false, trainingEnable:[{},{},{},{},{}], city:[{},{},{},{},{}] },
    troopCap  : { city:[{},{},{},{},{}] },
    messages  : { lastRead:0, missing:0 },
    objStats  : null,
    objMarches  : {},
    autoColInt  : 8,
    isDefending  : false,
    trainTab  : 0,
    trainQChoice : 'min_housing',
    tJobs   : [],
    rJobs   : [],
    buildTimer  : null,
    researchTimer : null,
    trainTimer  : null,
    autoCollect  : { enabled:false, lastTime:0, delay:1, unit:3600 },
    verboseLog  : { enabled:false },
    autoRefresh  : { enabled:false, lastTime:0, delay:15 }
   }
  });
  
  verboseLog('Data Structure v' + DATA_VERSION);
  
  
  /**  Check basic initialization
  ***************************************/
  function stepStarting (currentStep, retry) {

   var retry = retry || 0;
   var wait_time = Math.randRange(2000,4000);
   var errorMsg;

   StepTimeBar.update(currentStep);
   
   switch (currentStep) {
   
    /**  Translation Initialization
    ***************************************/
    case 1:
     Translation.init(function (r) {
      if (r.ok) {
       var message = 'Translation Matrix Successfully initialised';
       verboseLog(message);
       console.log(message);
       setTimeout(stepStarting, wait_time, currentStep+1);
       /*
       //This is only for programming purposes
       var a=[];
       for (var i=0; i < Translation._section.length; i++){
        for (n in Translation.object[Translation._section[i]]){
         a.push([n,Translation[Translation._section[i]](n)].join(' = "') + '"');
         //console.log([n,Translation[Translation._section[i]](n)].join(' = "') + '"');
        }
       }
       actionLog(a.join('<br>'));
       */
      } else {
       if (r.status == 509){
        wait_time = 600000;
        $startUpBox.append('<br><b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        verboseLog('<b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        setTimeout(stepStarting, wait_time, currentStep, ++retry);
        return;
       }
       errorMsg = r.errmsg;
       console.log('stepStarting:: Translation retry ' + retry);
       setTimeout(stepStarting, wait_time, currentStep, ++retry);
      }
     });
    break;
    
    /**  Manifest Initialization
    ***************************************/
    case 2:
     Manifest.init(function (r) {
      if (r.ok) {
       var message = 'Manifest Successfully initialised';
       verboseLog(message);
       console.log(message);
       setTimeout(stepStarting, wait_time, currentStep+1);
      } else {
       if (r.status == 509){
        wait_time = 600000;
        $startUpBox.append('<br><b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        verboseLog('<b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        setTimeout(stepStarting, wait_time, currentStep, ++retry);
        return;
       }
       errorMsg = r.errmsg;
       console.log('stepStarting:: Manifest retry ' + retry);
       setTimeout(stepStarting, wait_time, currentStep, ++retry);
      }
     });
    break;
    
    /**  Seed Initialization
    ***************************************/
    case 3:
     Seed.init(function (r) {
      if (r.ok) {
       var message = 'Seed Successfully initialised';
       verboseLog(message);
       console.log(message);
       setTimeout(stepStarting, wait_time, currentStep+1);
      } else {
       if (r.status == 509){
        wait_time = 600000;
        $startUpBox.append('<br><b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        verboseLog('<b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(wait_time/1000));
        setTimeout(stepStarting, wait_time, currentStep, ++retry);
        return;
       }
       errorMsg = r.errmsg;
       console.log('stepStarting:: Seed retry ' + retry);
       setTimeout(stepStarting, wait_time, currentStep, ++retry);
      }
     });
    break;
    
    /**  Seed Check Cities Numbers
    ***************************************/
    case 4:
     if( Seed.cityInit >= Seed.numCities) {
      StepTimeBar.stop();
      startPowerTools();
      return;
     }
     // it runs only in the case of incomplete data in fetchPlayer
     console.log('stepStarting:: Cities:' + Seed.cityInit + ' of ' + Seed.numCities);
     setTimeout(stepStarting, wait_time, 3, ++retry);
     console.log('stepStarting:: Seed Cities retry ' + retry);

    break;
     
   }
   
   // Retries Limit
   if(++retry > 20){
    $startUpBox.title(kFatalSeedTitle);
    $startUpBox.html(kFatalSeedMsg +'<br><br>'+errorMsg);
    return;
   }

  }
  
  stepStarting(1);
  
  
  
  
  /**  startPowerTools
  ***************************************/
  function startPowerTools() {
  
   $startUpBox.destroy();
   
   // Initialization
   AutoCollect.init ();
   AutoRefresh.init();
   Map.init ();
   Marches.init ();
   Messages.init ();
  
   // Create a new popup DIV for the main script window
   var width = Math.randRange(490,495);
   Data.options.mainBox.x = ( Data.options.mainBox.x > 0 ? Data.options.mainBox.x : parseInt(document.body.offsetWidth-(document.body.offsetWidth-760)/2-width/2) );
   
   $mainBox = dialogBox({
    id   : setUID('dialog-main-box'),
    dialogClass : UID['main-box'],
    position : [Data.options.mainBox.x, Data.options.mainBox.y],
    width  : width,
    height  : Math.randRange(780,785),
    draggable : Data.options.mainBox.draggable,
    title  : 'v' + SCRIPT_VERSION,
    buttons  : {},
    close  : function (){
        tabManager.hideTab();
        resetScript();
       },
    dragStop : function (event, ui) {
        var offset = $J(event.target).offset();
        //Data.options.mainBox.x = document.body.offsetWidth - offset.left - $J(event.target).outerWidth();
        Data.options.mainBox.x = offset.left;
        Data.options.mainBox.y = offset.top-24;
       }
   });
   
   // Create all the tabs and insert them into the main Popup
   tabManager.init($mainBox);
   tabManager.showTab();

   //Scrambled Title
   scrambledTitle();
   
   // Start event listeners to look for an unload event from Data Storage   
   window.addEventListener('unload', Data.onUnload, false);
   
   
   actionLog (SCRIPT_VERSION + ' ' +translate('Loaded'));
   verboseLog(SCRIPT_VERSION + ' Loaded');
  
   // Internal Functions
   
   function scrambledTitle(){
    var len = TITLE_WORDS.length-1;
    var w1 = Math.ceil (Math.random() * len);
    var w2 = Math.ceil (Math.random() * len);
    SCRIPT_TITLE = translate(TITLE_WORDS[w1]) +' '+ translate(TITLE_WORDS[w2]);
   }

  }
 } catch (e) {
  $startUpBox.title('ERROR!');
  $startUpBox.html(kInitErr +'<br><br>'+e);
  logit(inspect (e, 8, 1));
 }  
}




MyAjax = {

 RequestDOA : function (url, params, callback, isPost) {
  new MyAjax.Request (API_SERVER +'/'+ url, {
   useSignature: isPost,
   method  : (isPost ? 'POST' : 'GET'),
   params  : params,
   timeoutSecs : 45,
   
   onSuccess : function(r) {
    if (r.status == 200 && r.responseText) {
     if (url.indexOf(".xml") !== -1) {
      callback({ok:true, dat:r.responseText});
     } else {
      var jsonObj;
      try{
       jsonObj= JSON.parse(r.responseText);
      } catch (e) {}
      callback({ok:true, dat:jsonObj});
     }
    } else {
     callback({ok:false, errmsg:'The request was successful but no data was returned'});
    }
   },
  
   onFailure : function(r) {
    var res = {
     ok  : false,
     status : r.status,
     errmsg : r.statusText,
    };
    
    if (r.responseText) {
     res.dat = r.responseText;
    } else if (!r.status) {
     res.errmsg = 'This browser is not compatible at this time';
    }
    
    callback(res);
   },
   
   on403 : function(r){
    dialogError('<b>' + kFatalSeedTitle + '</b><br><br>\
     <font color="#C00"><b> ' + r.statusText + '</b></font><br><br>\
     <b>Previous Requirements</b><br>\
     <b>FIREFOX</b>\
     <ul>\
     <li>Download and install <a href="https://addons.mozilla.org/es-ES/firefox/addon/refcontrol/">RefControl</a></li>\
     <li>Once installed click Tools - RefControlOptions</li>\
     <li>Click Add Site and type in wonderhill.com</li>\
     <li>Check the Block - Send no referer radio box</li>\
     <li>Click OK and then OK again</li>\
     </ul>\
     <br>\
     <b>CHROME</b>\
     <ul>\
     <li>Right click on your "Chrome" icon (either on your Desktop or your Taskbar)</li>\
     <li>Choose properties</li>\
     <li>At the end of your target line, place these parameters: --no-referrers</li>\
     <li>Click OK</li>\
     </ul>\
     <br><br>\
     <a id="' + UID['support_link'] + '" href="" target="_blank">Bugs and Known Issues</a><br><br>\
     ');
   },
   
   on509 : function(r){
    dialogError('<b>ERROR 509</b><br><br>\
     <div style="text-align:center;">\
     <span style="color:#C00;font-size:12pt;"><b>Rate Limit Exceeded</b></span><br><br>\
     <b>This account has been blocked momentarily.<br>\
     Exceeded the maximum number of requests for hour.<br>\
     <br>TRY AGAIN LATER</b>\
     </div>');
   }
   
   
  });
 },
 
 Request : function (url, options) {
  var request, params, headers = {}, timeout, h;
  
  request = new XMLHttpRequest();
  
  request.onreadystatechange = function () {
   if (request.readyState == 4) {
    clearTimeout(timeout); 
    var response = {
     responseText : request.responseText,
     status   : request.status,
     statusText  : request.statusText,
     request   : request
    }
    if ( (request.status >= 200 && request.status < 300) || request.status == 304) {
     if (options.onSuccess) options.onSuccess(response);
    } else {
     if (options.onFailure) options.onFailure(response);
     if (options['on' + request.status]) options['on' + request.status](response);
    }
   } 
  } 
  
  
  // Parse request parameters
  params = typeof options == 'string' ? options.params : Object.toQueryString(options.params).replace(/\_/g,'%5F');
   
  // Change Accept request header based on browser
  headers['Accept'] =  IS_CHROME ? '*/*' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
  
  // Add request header specific to POST request only
  if (options.useSignature) {
   headers['content-type'] = 'application/x-www-form-urlencoded';
   headers['X-S3-AWS'] = SHA1("Dracunculiasis" + params + "LandCrocodile" + url  + "Bevar-Asp");
  } 
  
  // Merge headers with option.headers
  //$J.extend(headers, options.headers || { });
  
  // Open Request
  if (options.method == 'GET') {
   url += (url.include('?') ? '&' : '?') + params;
  }
  request.open(options.method, url, true);
  
  // Add request headers to ajax request
  for (h in headers) {
   request.setRequestHeader(h, headers[h]);
  }
  
  // Start timeout check before request is sent
  if (options.timeoutSecs) {
   timeout = setTimeout( function() {
    request.abort();
    if (options.onFailure) {
     // CHECK: 599 is custom error code. See if better option exists.
     options.onFailure({
      responseText : null,
      status   : 599,
      statusText  : 'Request Timed Out',
      request   : request
     });
    }
   }, options.timeoutSecs*1000);
  }
  
  // Send request with params if POST otherwise just send request
  request.send(options.method == 'POST' ? params : null);
 },
 
 
 // Use a json to wrap the building upgrade job
 buildingUpgrade : function (cityId, buildingId, callback){
  var t = MyAjax;
  var p = {};
  p['user_id']  = USER_ID;
  p['dragon_heart'] = DRAGON_HEART;
  p['_session_id'] = SESSION_ID;
  p['_method']  = 'put';
  p['version']  = API_VERSION;
  p['timestamp']  = parseInt(serverTime());
  new MyAjax.RequestDOA ('cities/'+ cityId +'/buildings/'+ buildingId +'.json', p, mycb, true);
  function mycb (r){
   //logit ("BUILD RESPONSE:\n" + inspect (r, 10, 1));
   if (r.ok && r.dat.result) {
    if( r.dat.result.success) {
     Seed.checkAddJob (r.dat.result.job);
    } else {
     r.ok = false;
     r.errmsg = r.dat.result.errors[0];
    }
   }
   
   if (callback){
    callback (r);
   }
  }
 },
 
 collectResources : function (cityId, callback){
  var p = {};
  p['user_id']  = USER_ID;
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['version']  = API_VERSION;
  p['dragon_heart'] = DRAGON_HEART;
  new MyAjax.RequestDOA ('cities/'+ cityId +'/move_resources.json', p, mycb, true);
  function mycb (r){
   if ( r.ok ) {
    Seed.updateCity (r.dat.city);
   }
   else if (r.dat.result) {
    //r.ok = false;
    r.errmsg = r.dat.result.errors[0];
    verboseLog('<b>Auto-Collect</b> Error: ' + r.msg);
   }

   if (callback){
    callback (r.ok);
   }
  }
 },
 
 marchBusy : 0,
 marchSend : function (cityId, x, y, generalId, units, ownerId, callback) {
  var t = MyAjax;
  
  ++t.marchBusy;
  
  var u = {}
  var mt = false;
  var sendTroops = "{";
  for (var pu in units){
   if (units[pu] > 0) {
    u[pu] = units[pu];
    if (mt == true ){
     sendTroops += ',';
    }
    sendTroops += '"' + pu + '":' + units[pu];
    mt = true;
   }
  }
  sendTroops += "}";
  
  // Initialise POST data
  var p = {};
  p['march[march_type]'] = 'attack';
  p['march[y]']   = y;
  p['timestamp']   = parseInt(serverTime());
  p['march[units]']  = sendTroops;
  p['march[general_id]'] = generalId;
  p['version']   = API_VERSION;
  p['_method']   = 'post';
  p['dragon_heart']  = DRAGON_HEART;
  p['user_id']   = USER_ID;
  p['march[x]']   = x;
  p['_session_id']  = SESSION_ID;
  // Send request
  new MyAjax.RequestDOA ('cities/'+ cityId +'/marches.json', p, mycb, true);
  function mycb(r) {
   --t.marchBusy;
   
   verboseLog('MyAjax.marchSend< request was returned with a status of ' + r.ok );

   if (r.ok && !r.dat.errors) {
    if (r.dat.result && r.dat.result.success) {
     try {
      Seed.updateCity(r.dat.result.city);
      Seed.marches[r.dat.result.job.march_id].ownerId = ownerId;
     } catch (e) {
      WinLog.write ('***********'+ e);
     }
    }
    else if(r.dat.result) {
     r.ok = false;
     r.errmsg = r.dat.result.reason;
    }
   } else if (r.ok && r.dat.errors) {
    r.ok = false;
    r.errmsg = r.dat.errors;
   } else {
    r.ok =false;
   }
   
   r.status=r.status;
   
   if (callback){
    callback (r);
   }
  }
 },

 // This looks really cool, if it works
 marchRecall : function (cityId, marchId, callback){
  var t = MyAjax;
  var p = {};
  ++t.marchBusy;
  p['user_id']  = USER_ID;
  p['_session_id'] = SESSION_ID;
  p['_method']  = 'delete';
  p['dragon_heart'] = DRAGON_HEART;
  p['timestamp']  = parseInt(serverTime());
  p['version']  = API_VERSION;
  new MyAjax.RequestDOA ('cities/'+ cityId +'/marches/'+ marchId +'.json', p, mycb, true);
  function mycb (r){
   --t.marchBusy;
   //logit ("MARCH RESPONSE:\n" + inspect (r, 10, 1));
   if (r.ok && !r.dat.errors) {
    if (r.dat.result && r.dat.result.success){
     //logit (inspect (r, 9, 1));        
     Seed.updateCity(r.dat.result.city);
     Seed.marches[r.dat.result.job.march_id].status = 'retreating';
    } 
    else if(r.dat.result) {
     r.ok = false;
     r.errmsg = r.dat.result.errors[0];
    }
   } else if (r.ok && r.dat.errors) {
    r.ok = false;
    r.errmsg = r.dat.errors;
   }
   if (callback){
    callback (r);
   }
  }
 },
 
 messageDelete : function (ids, callback){
  var p = {}
  p['user_id']  = USER_ID;
  p['_method']  = 'delete';
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['ids']   = ids.join('|');
  p['dragon_heart'] = DRAGON_HEART;
  p['version']  = API_VERSION;
  new MyAjax.RequestDOA ('reports/bulk_delete.json', p, mycb, true);
  function mycb (r){
   if (r.ok && r.dat.result && r.dat.result.success) {
    r.ok = false;
   }
   else if (callback) {
    callback (null);
   }
  }
 },

 messageDetail : function (id, callback){
  var p = {}
  p['user_id']  = USER_ID;
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['version']  = API_VERSION;
  p['dragon_heart'] = DRAGON_HEART;
  new MyAjax.RequestDOA ('reports/'+ id +'.json',p , mycb, false);
  function mycb (r){
   if (r.ok && r.dat.result && r.dat.result.success) {
    if (callback){
     callback (r.dat.result);
    }
   } 
   else if (callback) {
    callback (null);
   }
  }
 },
 
 messageList : function (cat, callback){
  if (!cat){
   cat = 'all';
  }
  var p = {}
  p['user_id']  = USER_ID;
  p['dragon_heart'] = DRAGON_HEART;
  p['count']   = 12;
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['category']  = cat;
  p['page']   = 1;
  p['version']  = API_VERSION;
  new MyAjax.RequestDOA ('reports.json', p, mycb, false);
  function mycb (r){
   if (r.ok && r.dat.result && r.dat.result.success) {
    if (callback){
     callback (r.dat.result.report_notifications);
    }
   } else if (callback) {
    callback (null);
   }
  }
 },

 researchStart : function (cityId, research_type, callback){
  var t = MyAjax;
  var p = {};
  p['user_id']  = USER_ID;
  p['_method']  = 'post';
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['research[research_type]'] = research_type;
  p['dragon_heart'] = DRAGON_HEART;
  p['version']  = API_VERSION;
  new MyAjax.RequestDOA ('cities/'+ cityId +'/researches.json', p, mycb, true);
  function mycb (r){
   //logit ("RESEARCH RESPONSE:\n" + inspect (r, 10, 1));
   if (r.ok && r.dat.result) {
    if (r.dat.result.success) {
     Seed.checkAddJob (r.dat.result.job);
    }
    else {
     r.ok = false;
     r.errmsg = r.dat.result.errors[0];
    }
   }
   
   
   if (callback){
    callback (r);
   }
  }
 },
 
 troopTraining : function (unit_type, troopQty, cityId, callback){
  var t = MyAjax;
  var p = {};
  p['user_id']    = USER_ID;
  p['_method']    = 'post';
  p['timestamp']    = parseInt(serverTime());
  p['_session_id']   = SESSION_ID;
  p['units[quantity]']  = troopQty;
  p['units[unit_type]'] = unit_type;
  p['dragon_heart']   = DRAGON_HEART;
  p['version']    = API_VERSION;
  new MyAjax.RequestDOA ('cities/'+ cityId +'/units.json', p, mycb, true);
  function mycb (r){
   //logit ("Troop Training Response:\n" + inspect (r, 10, 1));
   if (r.ok && r.dat.result) {
    if (r.dat.result.success) {
     Seed.checkAddJob (r.dat.result.job);
    }
    else {
     r.ok = false;
     r.errmsg = r.dat.result.errors[0];
    }
   }
   
   if (callback){
    callback (r);
   }
  }
 },

 wildernessesAbandon : function (cityId, x, y, callback){
  var t = MyAjax;
  var p = {};
  p['user_id']  = USER_ID;
  p['x']    = x;
  p['_method']  = 'delete';
  p['y']    = y;
  p['timestamp']  = parseInt(serverTime());
  p['_session_id'] = SESSION_ID;
  p['dragon_heart'] = DRAGON_HEART;
  p['version']  = API_VERSION;
  new MyAjax.RequestDOA ('cities/'+ cityId +'/wildernesses/abandon.json', p, mycb, true);
  function mycb (r){

   if (r.ok && !r.dat.errors) {
    if (r.dat.result && r.dat.result.success){
     Seed.updateCity(r.dat.result.city);
     //Seed.marches[r.dat.result.job.march_id].status = 'retreating';
    } 
    else if(r.dat.result) {
     r.ok = false;
     r.errmsg = r.dat.result.errors[0];
    }
   } else if (r.ok && r.dat.errors) {
    r.ok = false;
    r.errmsg = r.dat.errors;
   }
   if (callback){
    callback (r);
   }
  }
 }

 

 
}; // END MyAjax


// Added the autocollection interval from the select menu
AutoCollect = {
 init : function (){
  var t = AutoCollect;
  t.setEnable (Data.options.autoCollect.enabled);
 },
 
 setEnable : function (onOff){
  var t = AutoCollect;
  clearTimeout (t.timer);
  Data.options.autoCollect.enabled = onOff;
  if (onOff){
   var time = (Data.options.autoCollect.delay*Data.options.autoCollect.unit) - serverTime() + Data.options.autoCollect.lastTime;
   if (time <= 0){
    t.doit ();
   } else {
    t.timer = setTimeout (t.doit, time*1000);
   }
  }
 },
 
 doit : function (){
  var t = AutoCollect;
  Data.options.autoCollect.lastTime = serverTime();
  for (var out=1; out<Seed.cities.length; out++){
   collect (out, out*30000);
  }
  t.timer = setTimeout (t.doit, ((Data.options.autoCollect.delay*Data.options.autoCollect.unit) + (Math.random()*120))*1000);
  function collect (cityIdx, delay){
   setTimeout (function(){
    MyAjax.collectResources (Seed.cities[cityIdx].id);
    actionLog (translate('Collected resources at outpost')+ ' #'+ cityIdx);
   }, delay);
  }
 }
}; // END Auto Collect

AutoRefresh = {
 _timer   : null,
 _currMouseXY : [0,0],
 _lastMouseXY : [0,0],
 
 init : function () {
  var t = AutoRefresh;
  t.setEnable(Data.options.autoRefresh.enabled);
 },
 
 setEnable : function (onOff) {
  var t = AutoRefresh;
  Data.options.autoRefresh.enabled = onOff;
  if(Data.options.autoRefresh.enabled){
   Data.options.autoRefresh.lastTime = parseInt(serverTime());
   window.addEventListener('mousemove', t.onMouseMove, false);
   t.onTimeout();
  } else {
   window.removeEventListener('mousemove', t.onMouseMove, false);
  }
 },
 
 setDelay : function (delay) {
  var t = AutoRefresh;
  Data.options.autoRefresh.delay = delay;
 },
 
 onMouseMove : function (event){
  AutoRefresh._currMouseXY = [event.clientX, event.clientY];
 },
 
 onTimeout : function (){
  var t = AutoRefresh;
  clearTimeout(t._timer);
  
  if ( t._currMouseXY.join() != t._lastMouseXY.join() ) {
   Data.options.autoRefresh.lastTime = parseInt(serverTime());
   t._lastMouseXY = [].concat(t._currMouseXY);
  }
  
 
  if(parseInt(serverTime()) - Data.options.autoRefresh.lastTime > parseInt(Data.options.autoRefresh.delay)*60){
   document.location = document.location.href;
  }
  
  
  if(Data.options.autoRefresh.enabled){
   t._timer = setTimeout(t.onTimeout, 30000);
  }
 }
};// END AutoRefresh


Buildings = {
 getList : function (cityIdx, type){
  var ret = [];
  for (var i=0; i<Seed.cities[cityIdx].buildings.length; i++){
   if (Seed.cities[cityIdx].buildings[i].type == type){
    ret.push (Seed.cities[cityIdx].buildings[i]);
   }
  }
  return ret;
 },
 getLevel : function (cityIdx, type){
  var x = Buildings.getList(cityIdx, type);
  if (x.length < 1){
   return 0;
  }
  x.sort(function(a,b){return a.level - b.level;});
  return x[0].level;
 },
 getById : function (cityIdx, bid){
  for (var i=0; i < Seed.cities[cityIdx].buildings.length; i++){
   if (Seed.cities[cityIdx].buildings[i].id == bid){
    return (Seed.cities[cityIdx].buildings[i]);
   }
  }
  return null;
 }
}; // END Building



Data = {
 _log  : [ [], [] ],
 _defaults : {},
 
 init : function (obj) {
  try {
  
   //Saves defaults properties
   Data._defaults.mergeWith( obj || {} );
   
   for (var itemName in obj)
   {
    
    // Checks if the object is already defined in the Data Object
    if ( Data[itemName] == undefined ){
     //  Assign default object properties, if defined, otherwise an empty object
     Data[itemName] = obj[itemName] != undefined ? obj[itemName] : {};
    }
    
    // Load the data stored, of the current item from localStorage
    var storedObject = Data.getObject(itemName);

    // Check if the default object is really an object
    if ( Data[itemName] != null && typeof(Data[itemName]) == "object" )
    {
     // Assign the properties of stored objeto into the default object, overwriting the values
     Data[itemName].mergeWith( storedObject );
    } else if ( storedObject != '' ) {
     Data[itemName] = storedObject;
    } 
   }
  } catch (e) {
   alert ('This browser does not support LocalStorage\n\n'+e);
   return false;
  }
 },
 
 clearStorage : function(){
  localStorage.clear();
  for (var itemName in Data._defaults){
   if ( Data._defaults[itemName] != null && typeof(Data._defaults[itemName]) == "object" )
   {
    Data[itemName].mergeWith( Data._defaults[itemName] );
   } else {
    Data[itemName] = Data._defaults[itemName];
   } 
  }
  actionLog('localStorage Deleted!');
 },
 
 getObject : function(key) {
  var item = localStorage.getItem([SERVER_ID,USER_ID,key].join('_'));
  return (item||'').charAt(0) == '{' ? JSON.parse(item||'{}') : eval(item);
 },
 
 setObject : function(key, value) {
  try {
   localStorage.setItem([SERVER_ID,USER_ID,key].join('_'), JSON.stringify(value));
  } catch(e){
   if (e == QUOTA_EXCEEDED_ERR) {
    dialogError ('<p style="font-size:12pt;">'+translate('LocalStorage')+'<p><br/>'+translate('Quota exceeded')+'!<br/>'+translate('Please, delete the cache and persistent data in your browser'));
   }
  }
 },

 onUnload : function () {
  logit('Save Data in localStorage');
  verboseLog('Save Data in localStorage');
  for (var itemName in Data._defaults){
   Data.setObject ( itemName, Data[itemName] );
  }
 }
 
 
}; //END Data


Manifest = {
 data : {},
 
 init : function (callback) {
  Manifest.fetchManifest(function (res) {
   if (res.ok) {
    verboseLog('Manifest was Successfully requested from the server');
   }
   if (callback){
    callback(res);
   }
  });
 },
 
 fetchManifest : function (callback) {
  var now = new Date().getTime() / 1000;
  var params = {};
  params['user_id']  = USER_ID;
  params['_session_id'] = SESSION_ID;
  params['version']  = API_VERSION;
  params['timestamp']  = parseInt(serverTime());
  params['dragon_heart'] = DRAGON_HEART;
  
  new MyAjax.RequestDOA ('manifest.json', params, function (res) {
   if (res.ok && !res.dat.errors) {
    Manifest.data = res.dat; // This holds the entire Manifest JSON data parsed as an object 
    try {
     Manifest.updateManifest();
    } catch (e) {
     res.ok = false;
     res.errmsg = '<b>fetchManifest</b> when calling updateManifest returned this error: ' + e.toString();
    }
   } else if (res.ok && res.dat.errors) {
    res.ok = false;
    res.errmsg = res.dat.errors;
   }
   
   if (callback){
    callback(res);
   }
  }, false);
 },
 
 buildings : {
  byCityType : function (cityType, buildable, order) {
   var buildings = Manifest.data.buildings;
   var i, j, res = [];
   if (!buildable) {
    buildable = 'all';
   }
   if (!cityType) {
    cityType = 'all';
   }
   if (buildings.length > 0) {
    for (i = 0; i < buildings.length; i = i + 1) {
     if (buildings[i].buildable == buildable || buildable.toLowerCase() == 'all') {
      if (buildings[i].city_type.length > 0) {
       for (j = 0; j < buildings[i].city_type.length; j = j + 1) {
        if (buildings[i].city_type[j] == cityType.toLowerCase() || cityType.toLowerCase() == 'all') {
         res[res.length] = buildings[i];
         break;
        }
       }
      }
     }
    }
   }
   if (order) {
    res = Manifest.buildings.sortBy(res, order);
   }
   return res;
  },
  
  byLocation : function (location, buildable, order) {
   var buildings = Manifest.data.buildings;
   var i, res = [];
   if (!buildable) {
    buildable = 'all';
   }
   if (!location) {
    cityType = 'all';
   }
   if (buildings.length > 0) {
    for (i = 0; i < buildings.length; i = i + 1) {
     if (buildings[i].buildable == buildable || buildable.toLowerCase() == 'all') {
      if (buildings[i].location == location.toLowerCase() || location.toLowerCase() == 'all') {
       res[res.length] = buildings[i];
      }
     }
    }
   }
   if (order) {
    res = Manifest.buildings.sortBy(res, order);
   }
   return res;
  },
  
  sortBy : function (data, order) {
   var orderBy;
   if (!order) {
    order = {alphabetical: 'asc'};
   }
   for (orderBy in order) {
    switch (orderBy) {
    case 'alphabetical' :
     orderAlphabetical(order[orderBy]);
     break;
    case 'buildable' :
     orderBuildable(order[orderBy]);
     break;
    case 'location' :
     orderLocation(order[orderBy]);
     break;
    }
   }
   return data;
   
   function orderAlphabetical(order) {
    if (order.toLowerCase() == 'asc') {
     data.sort(function (a, b) {
      var typeA = a.type.toLowerCase(), typeB = b.type.toLowerCase();
      if (typeA < typeB) {return -1}
      if (typeA > typeB) {return 1}
      return 0;
     });
    } else if (order.toLowerCase() == 'desc') {
     data.sort(function (a, b) {
      var typeA = a.type.toLowerCase(), typeB = b.type.toLowerCase();
      if (typeA > typeB) {return -1}
      if (typeA < typeB) {return 1}
      return 0;
     });
    }
   }
  
   function orderBuildable(order) {
    if (order == true) {
     data.sort(function (a, b) {
      var buildableA = a.buildable, buildableB = b.buildable;
      if (buildableA < buildableB) {return -1}
      if (buildableA > buildableB) {return 1}
      return 0;
     });
    } else if (order == false) {
     data.sort(function (a, b) {
      var buildableA = a.buildable, buildableB = b.buildable;
      if (buildableA > buildableB) {return -1}
      if (buildableA < buildableB) {return 1}
      return 0;
     });
    }
   }

   
   function orderLocation(order) {
    if (order.toLowerCase() == 'city') {
     data.sort(function (a, b) {
      var locationA = a.location.toLowerCase(), locationB = b.location.toLowerCase();
      if (locationA < locationB) {return -1}
      if (locationA > locationB) {return 1}
      return 0;
     });
    } else if (order.toLowerCase() == 'field') {
     data.sort(function (a, b) {
      var locationA = a.location.toLowerCase(), locationB = b.location.toLowerCase();
      if (locationA > locationB) {return -1}
      if (locationA < locationB) {return 1}
      return 0;
     });
    }
   }
  },
 },
 
 building : function (type) { 
  console.log('Manifest.building');
  var b;
  
  if (type) {
   for (b = 0; b < Manifest.data.buildings.length; b = b + 1) {
   
   }
  } else {
   // Return an error message because no type was specificed
  }
 },
 
 updateManifest : function () {
  var i, j;
  var buildingManifest = Manifest.data.buildings;
  var researchManifest = Manifest.data.research;
  var troopManifest = Manifest.data.city.capital.units;
  
  // Initialise levels for each building & Save requirements
  for (i=0; i < buildingManifest.length; i++) {
   if (!Seed.requirements.building[buildingManifest[i].type]) {
    Seed.requirements.building[buildingManifest[i].type] = {};
   }
   if (!Seed.requirements.building[buildingManifest[i].type].level) {
    Seed.requirements.building[buildingManifest[i].type].level = [];
   }
   for (j=0; j < buildingManifest[i].levels.length; j++){
    Seed.requirements.building[buildingManifest[i].type].level[buildingManifest[i].levels[j].level] = buildingManifest[i].levels[j].requirements;
   }
  }


  // Initialise levels for each research & Save requirements
  for (i=0; i < researchManifest.length; i++) {
   if (!Seed.requirements.research[researchManifest[i].type]){
    Seed.requirements.research[researchManifest[i].type] = {};
   }
   if (!Seed.requirements.research[researchManifest[i].type].level) {
    Seed.requirements.research[researchManifest[i].type].level = [];
   }
   for (j=0; j < researchManifest[i].levels.length; j++) {
    //Seed.requirements.research[researchManifest[i].type].level[researchManifest[i].levels[j].level] = {};
    Seed.requirements.research[researchManifest[i].type].level[researchManifest[i].levels[j].level] = researchManifest[i].levels[j].requirements;
   }
  }
  
  // Initialise troops & Save requirements
  for (i=0; i < troopManifest.length; i++){
   if (!Seed.requirements.troop[troopManifest[i].type]) {
    Seed.requirements.troop[troopManifest[i].type] =[];
   }
   Seed.requirements.troop[troopManifest[i].type] = troopManifest[i].requirements;
  }
 }

};

Marches = {
 output : {
  attacks: {},
  waves  : {}
 },
 
 init : function () {
  var t = Marches;
 
  Data.init({
   countForLimit : 1,
   marches : {
    attacks : {},
    waves : {}
   },
   startAt : 0
  });
  
 },
 
 add : function (id, type){
  var t = Marches;
  var march = Seed.marches[id];
  if (march == null){
   logit ('***** March missing from seed: '+ id); 
   if (DEBUG_MARCHES){
    WinLog.write ('***** ERRROR march missing from seed: '+ id);
   }
  } 
  else {
   (Data.marches[type])[id] = march.cloneProps();
   if (DEBUG_MARCHES){
    WinLog.write ('Marches.add: ID='+ march.id +'  ('+ march.x +','+ march.y +') General:'+ march.general.id);
   }
  }
 },

 remove : function (id, type){   
  var t = Marches;
  if (id) {
   delete ((Data.marches[type])[id]);
  }
 },

 checkTimer : null,
 
 check : function (){
  var t = Marches;
  var now = parseInt(serverTime());
  clearTimeout (t.checkTimer);
  for (var type in Data.marches){
   var marches = Data.marches[type];
   for (var id in marches)
   {
    if (parseInt(marches[id].run_at) < (now-40))
    {
     
     if (marches[id].retry)
     {
      ++Data.options.messages.missing;
      
      // Prevent errors in the marches queuing ?
      if(MyAjax.marchBusy){
       --t.marchBusy;
      }
      
      logit ('March report never received! (now='+ now +')\n'+ inspect (marches[id], 6, 1));    
      if (DEBUG_MARCHES){
       WinLog.write ('March report never received! (now='+ now +')\n'+ inspect (marches[id], 6, 1));    
      }
      t.remove(id, type);
      try {
       delete Seed.marches[id];
      } catch(e){}
      --Seed.numMarches;
     }
     else {
      marches[id].retry = true;
      Messages.checkMessages();
     }
    }
   }
  }
  t.checkTimer = setTimeout (t.check, Math.randRange(60000,90000));
 },
 
 updateTable : function(table, type){
  var t = Marches;
  var now = parseInt(serverTime());
  
  var output = t.output[type];
  
  // Changes the state of the marches completed
  for (var id in output)
  {
   if ( (Data.marches[type])[id] == undefined )
   {
    output[id].status = 0;
   }
  }
  
 
  /* 
  * NOTE: We use this dual system, one to incorporate for the first time the values and another
  * to update it, because we don't want to miss the event listener of the button to cancel the marches
  */
  for (var id in Seed.marches)
  {
   var iRow, iCell;

   if ( (Data.marches[type])[id] == undefined) {
    t.add(id, 'attacks');
   }
   
   output = t.output[type];
   
   var march = Seed.marches[id];

   Seed.checkMarchStatus(march);
   
   var isReturning = (march.status == 'returning' || (march.last_status != undefined && march.last_status == 'returning'));
   
   var time = march.run_at - now;
   var str_time;
   
   if (time < 0){
    str_time = '...';
    //march.status = isReturning ? 'complete' : 'waiting'
   } else if (isNaN (time)){
    str_time = '---';
   } else {
    str_time = timeFormat(time, true);
   }

   
   /* Insert Row Values
   /*******************/
   if (output[id] == undefined && time) {
    
    output[id] = {row:table.rows.length||0};
    
    iRow = table.insertRow(-1);
    iRow.setAttribute('ref', id);
    iRow.title = [
       '(' + march.general.name + ')'
       ,march.target
       ,march.terrain_level
       ,'[' + march.x + '/' + march.y + ']\n'
       ,JSON.stringify(march.units).replace(/[\{\}]/g,'').replace(/,/g,'\n')
     ].join(' ');
  
    // Returning case
    if(isReturning) {
     output[id].status = 2; // Returning mode
    
     // march Status
     iCell = iRow.insertCell(-1);
     iCell.innerHTML = '<b>'+ translate(march.status).capitalize() + ':</b>';
     // march Target
     iCell = iRow.insertCell(-1);
     iCell.innerHTML = march.target  + '&nbsp;';
     // march Coords
     iCell = iRow.insertCell(-1);
     iCell.style.textAlign = "right";
     iCell.innerHTML = '&nbsp;<b>&lt;</b>&nbsp;';
     // march Time
     iCell = iRow.insertCell(-1);
     iCell.innerHTML = '&nbsp;';
     // march Recall Button
     iCell = iRow.insertCell(-1);
     iCell.style.textAlign = "right";
     iCell.innerHTML = str_time;
    }
    
    // Marching case
    else {
     output[id].status = 1; // Marching mode
    
     // march Status
     iCell = iRow.insertCell(-1);
     iCell.innerHTML = '<b>' + translate(march.status).capitalize() + ':</b>';
     // march Target
     iCell = iRow.insertCell(-1);
     iCell.innerHTML = march.target  + '&nbsp;' + march.terrain_level + '&nbsp;';
     // march Coords
     iCell = iRow.insertCell(-1);
     iCell.style.textAlign = "right";
     iCell.innerHTML = '<span class=jewel> [' + march.x +'/'+ march.y +']</span>&nbsp;<b>&gt;</b>&nbsp;';
     // march Time
     iCell = iRow.insertCell(-1);
     iCell.style.textAlign = "right";
     iCell.innerHTML =  str_time;

     // march Recall Button
     if (Data.options.verboseLog.enabled || Seed.marches[id].march_type != "Transport") {
      iCell = iRow.insertCell(-1);
      var button = document.createElement('input');
      button.type = 'button';
      button.className = UID['bnt_red'] + ' thin';
      button.value = translate('Cancel');
      $J(button).click (function(event){
       button.disabled = true;
       button.style.display = 'none';
       MyAjax.marchRecall(march.city_id, march.id, function (r) {
        if (r.ok && r.dat.result.success) {
         Seed.marches[id].status = 'returning';
         (Data.marches[type])[id].status = 'returning';
        }
       });
      });
      iCell.appendChild(button);
     }
    }
    
   }
   
   /* Upgrade Row Values
   /*******************/
   else {
   
    if(output[id] == undefined) continue;
   
    iRow = table.rows[ output[id].row ];
    
    if(iRow == undefined){
     delete output[id];
     continue;
    }

    // Row Status cases
    switch (output[id].status) {
     // Finish state
     case 0:
      iRow.style.display = 'none';
      output[id].status = -1;
      continue;
     break
     // Marching state (Waiting for returning)
     case 1:
     case 2:
      if ( isReturning )
      {
       output[id].status = 3; // Change to Returning state
       // march Status
       iRow.cells[0].innerHTML = '<b>'+ translate(march.status).capitalize() + ':</b>';
       // march Target
       iRow.cells[1].innerHTML = march.target;
       // march Coords
       iRow.cells[2].innerHTML = '&nbsp;<b>&lt;</b>&nbsp;';
       // march Recall Button
       iRow.cells[4].innerHTML = '';
      }
      else if ( (isNaN(time) || time < 0) && output[id].status == 1)
      {
       if ( (march.status == 'marching' || march.status == 'encamped') &&
          march.terrain_type && !(/(Anthropus|City|Outpost|Bog)/.test(march.terrain_type))
        ) {
        output[id].status = 2; // Change to Waiting for Returning (Action Taken)
       
        // Abadon Wildernesses
        if ( Data.options.objAttack.abandonWildernesses ) {
         setTimeout(function(){
          MyAjax.wildernessesAbandon(march.city_id, march.x, march.y, function (r) {
           if (r.ok && r.dat.result.success) {
            Seed.marches[id].status = 'returning';
            (Data.marches[type])[id].status = 'returning';
           }
          });
         }, Math.randRange(5000,10000));
         // Clear March Recall Button
         iRow.cells[4].innerHTML = '';
        }
        
        // Recall Troops from Encamped Wildernesses
        else if ( Data.options.objAttack.recallEncamped ){
         setTimeout(function(){
          MyAjax.marchRecall(march.city_id, march.id, function (r) {
           if (r.ok && r.dat.result.success) {
            Seed.marches[id].status = 'returning';
            (Data.marches[type])[id].status = 'returning';
           }
          });
         }, Math.randRange(5000,10000));
         // Clear March Recall Button
         iRow.cells[4].innerHTML = '';
        }
       }
      }
     break;
     // Returning state (Waiting for finish)
     case 3:
      if( isNaN(time) || time < 0 ) {
       output[id].status = 0; // Change to Finish state
       /*
       try {
        if (output[id].row) table.deleteRow (output[id].row);
        if (output[id]) delete output[id];
       } catch(e){}
       */
      }
     break
    }
    
    // march Time
    iRow.cells[3].innerHTML = str_time;
   }
  }
 }
};

Map = {
 typeName : {
  'A':'AnthropusCamp',
  'B':'Bog',
  'F':'Forest',
  'G':'Grassland',
  'H':'Hill',
  'L':'Lake',
  'M':'Mountain',
  'P':'Plain',
  'C':'City',
  'O':'Outpost',
  'W':'Wildernesses',
  'X':'Fog',
 },
 _lastPos : {x:0, y:0},
 
 
 init : function () {
  var t = Map;
  
  t._lastPos.x = Seed.cities[0].x || 0;
  t._lastPos.y = Seed.cities[0].y || 0;
 
  Data.init({
   map : {
    terrains: {
     AnthropusCamp:[],
     Bog    :[],
     Forest   :[],
     Grassland  :[],
     Hill   :[],
     Lake   :[],
     Mountain  :[],
     Plain   :[],
     Fog    :[],
     City   :[],
     Outpost   :[],
     Wildernesses :[]
    },
    byCoords:{},
    radius : 30,
    x  : 0,
    y  : 0,
    targets : [],
    selected: kAnthropusCamp
   }
  });
  
  // Check Our Coords
  t.checkOurCoords();
 },
 
 scanMap : function (x, y, radius, callback){
  var t = Map;
  t.terrains = {
   AnthropusCamp:[],
   Bog    :[],
   Forest   :[],
   Grassland  :[],
   Hill   :[],
   Lake   :[],
   Mountain  :[],
   Plain   :[],
   Fog    :[],
   City   :[],
   Outpost   :[],
   Wildernesses :[]
  }; 
  
  t.centerX = x;
  t.centerY = y;
  t.firstX = t.normalize (x-radius+7);
  t.firstY =  t.normalize (y-radius+7);
  t.curIX = t.curIY = 0;
  t.widthI = parseInt(((radius*2)+14)/15);
  t.radius = radius;
  t.callback = callback; 

  t.steps = parseInt(t.widthI*t.widthI);
  t.step = 1;
  t.scanProgress = parseInt(t.step*100/t.steps);
  
  t.callback ({done:false,init:true});
 
  var params = {};
  params['user_id']  = USER_ID;
  params['x']    = t.firstX;
  params['y']    = t.firstY;
  params['timestamp']  = parseInt(serverTime());
  params['_session_id'] = SESSION_ID;
  params['dragon_heart'] = DRAGON_HEART;
  params['version']  = API_VERSION;  
  new MyAjax.RequestDOA ('map.json', params, t.gotMap, false);
 },  
 
 gotMap : function (r){
  var t = Map;
  var d = Data.map;

  if (!r.ok){
   t.callback (null);    // error !?!
   return;
  }
  
  // Terrains
  for (var i=0; i < r.dat.terrain.length; i++)
  {
   for (var j=0; j < r.dat.terrain[i].length; j++)
   {
    var value = r.dat.terrain[i][j];

    var type = value[0];
    
    if (type == 'City' || type == 'Outpost'){
     continue;
    }
    
    var x = value[2];
    var y = value[3];
    var xy = x + ',' + y;
    
    if (Data.map.byCoords[xy]) continue;
    
    var d = getDistance (Data.map.x, Data.map.y, x, y);

    // We use very small shortcuts because LocalStorage size available is limited
    var obj = {
     x : value[2], 
     y : value[3], 
     d : d, 
     t : type=='Fog'?'X':type.charAt(0), 
     l : value[1],
     at : 1,
     lst : 0
    };
    
    Data.map.terrains[type].push(obj); // Save in LocalStorage
    
    t.terrains[type].push(obj); // Save in Temporal Object
    
    Data.map.byCoords[xy] = {
     t : type.charAt(0),      // Save Terrain Type
     n : Data.map.terrains[type].length-1 // Save Terrain Index Number
    };
   }
  }
  
 
  //Cities
  for (var i=0; i < r.dat.map_cities.length; i++)
  {
   var target = r.dat.map_cities[i];

   var xy = target.x + ',' + target.y;
   
   if (Data.map.byCoords[xy]) continue; 
   
   // Skip our alliance or owner
   if ( (Seed.player.id == target.player.id) ||
    (Seed.player.alliance && target.player.alliance && Seed.player.alliance.id == target.player.alliance.id)
    ) {
    continue;
   }
   
   var d = getDistance(Data.map.x, Data.map.y, target.x, target.y);

   // We use very small shortcuts because LocalStorage size available is limited
   var obj = {
    x : target.x,
    y : target.y,
    d : d,
    t : target.type.charAt(0),
    l : target.level,
    id : target.id,
    n : target.name,
    pId : target.player.id,
    pN : target.player.name,
    pL : target.player.level,
    pR : target.player.race,
    //pT : target.player.type,
    pM : target.player.might,
    at : (target.player.alliance ? 0 : 1),
    lst : 0
   };
   
   if(target.player.alliance){
    obj.aId = target.player.alliance.id;
    obj.aN = target.player.alliance.name;
   }
   
   if(target.outpost_type){
    obj.lf = target.life;
    obj.mlf = target.maximum_life;
    obj.rR = target.recovery_rate;
    obj.oT = target.outpost_type;
   }
   
   var type = (target.type == 'Capital' ? 'City' : 'Outpost');

   Data.map.terrains[type].push(obj); // Save in LocalStorage
    
   t.terrains[type].push(obj); // Save in Temporal Object
   
   Data.map.byCoords[xy] = {
    t : type.charAt(0),      // Save Terrain Type
    n : Data.map.terrains[type].length-1 // Save Index Number
   };
   
  }
  
  
  //Wildernesses
  for (var i=0; i < r.dat.city_wildernesses.length; i++)
  {
   var target = r.dat.city_wildernesses[i];
   
   var xy = target.x+','+target.y;
   
   if (Data.map.byCoords[xy]  && Data.map.byCoords[xy].t == 'W') continue;
   
   var d = getDistance(Data.map.x, Data.map.y, target.x, target.y);
   
   var type = 'Wildernesses';
   var level = 0;
   
   if (Data.map.byCoords[xy]){
    type = Map.typeName[Data.map.byCoords[xy].t];
    var terrain = Data.map.terrains[type];
    if( terrain[Data.map.byCoords[xy].n] ) {
     level = terrain[Data.map.byCoords[xy].n].l;
     terrain[Data.map.byCoords[xy].n].at=0;
    }
   }
   
   // We use very small shortcuts because LocalStorage size available is limited
   var obj = {
    x : target.x,
    y : target.y,
    d : d,
    t : type.charAt(0),
    l : level,
    id : target.id,
    n : target.name,
    pId : target.player.id,
    pN : target.player.name,
    pL : target.player.level,
    pR : target.player.race,
    //pT : target.player.type,
    pM : target.player.might,
    at : (target.player.alliance ? 0 : 1),
    lst : 0
   };
   
   if(target.player.alliance){
    obj.aId = target.player.alliance.id;
    obj.aN = target.player.alliance.name;
   }
   
   // Set to Not-Attacable our alliance or owner Wilderness
   if ( (Seed.player.id == target.player.id) ||
    (Seed.player.alliance && target.player.alliance && Seed.player.alliance.id == target.player.alliance.id)
    ) {
    obj.at = 0;
   }
   
   
   Data.map.terrains.Wildernesses.push(obj); // Save for LocalStorage
    
   t.terrains.Wildernesses.push(obj); // Save in Temporal Object
   
   Data.map.byCoords[xy] = {
    t : 'W',         // Save Terrain Type
    n : Data.map.terrains.Wildernesses.length-1 // Save Index Number
   };
  }

  
  if (++t.curIX >= t.widthI){
   t.curIX = 0;
   if (++t.curIY >= t.widthI){
    t.callback ({done:true,terrains:t.terrains});
    return;
   }
  }
  
  t.step = t.step + 1;
  t.scanProgress = parseInt(t.step*100/t.steps);
  
  t.callback ({done:false});
  
  var timefix = parseInt(Data.map.radius*50);

  setTimeout (function(){
   var params = {};
   params['user_id']  = USER_ID;
   params['x']    = t.normalize(t.firstX+(t.curIX*15));
   params['y']    = t.normalize(t.firstY+(t.curIY*15));
   params['timestamp']  = parseInt(serverTime());
   params['_session_id'] = SESSION_ID;
   params['dragon_heart'] = DRAGON_HEART;
   params['version']  = API_VERSION;
   new MyAjax.RequestDOA ('map.json', params, t.gotMap, false);
  }, Math.randRange(2000+timefix, 4000+timefix) );
 },
 
 toCSV : function(terrains,type,structure){
  if (terrains[type] == undefined) {
   return;
  }
  if(structure == undefined){
   structure = [
    'aN',
    'pN',
    'x',
    'y',
    'pL',
    'pM',
    'name',
    'l',
    't',
    'mlf'
   ];
  }
  var csv = [];
  // headers
  csv.push(structure.join(';').replace(/\"/,''));
  // structure
  console.log(terrains[type].length);
  for(var i=0; i < terrains[type].length; i++){
   console.log(terrains[type].name);
   var row='';
   for(var j=0; j < structure.length; j++){
    if ( (terrains[type])[structure[j]] != undefined){
     row += (terrains[type])[structure[j]];
    }
    row += ';';
   }
   csv.push(row);
  }
  return csv;
 },
 
 getTargetByCoords : function(x, y, callback){
  var xy = x + ',' + y;
  if(Data.map.byCoords[xy]){
   var type = Map.typeName[Data.map.byCoords[xy].t];
   var index = Data.map.byCoords[xy].n;
   if(callback) callback(Data.map.terrains[type][index]);
   return Data.map.terrains[type][index];
  }
  Map.scanMap(x, y, 1, function(res){
   if(res.done){
    for (var type in res.terrains){
     for (var i=0; i < res.terrains[type].length; i++){
      if ( (res.terrains[type])[i].x == x && (res.terrains[type])[i].y == y){
       if(callback) callback( (res.terrains[type])[i] );
       return;
      }
     }
    }
   } else {
    if(callback) callback(false);
    return;
   }
  });
 },
 
 checkOurCoords : function(){
  var t = Map;
  if (Data.map.x != Seed.cities[0].x || Data.map.y != Seed.cities[0].y){
   
   Data.map.x = Seed.cities[0].x;
   Data.map.y = Seed.cities[0].y;
   
   // ReCalculates Distances
   for (var n in Data.map.terrains){
    var targets = Data.map.terrains[n];
    for (var i=0; i< targets.length; i++){
     targets[i].d = getDistance(Data.map.x, Data.map.y, targets[i].x, targets[i].y);
    }
   }
  }
 },
 
 simulateOpenMap : function(x, y, callback) {
  var t = Map;
  
  if(callback) setTimeout(callback, 1000);
  
  t.getTargetByCoords( x || t._lastPos.x, y || t._lastPos.y );
  
  t._lastPos.x = x || Seed.cities[0].x;
  t._lastPos.y = y || Seed.cities[0].y;
 },
 
 normalize : function (x){
  if (x > 750){
   x -= 750;
  }
  if (x < 0){
   x += 750;
  }
  return x;
 }
}; // End Map



// TODO: reduce n/w traffic - cache up requests
Messages = {
 readList : [],
 fetchTimer : null,
 lastQueued : 0,
 battleReportListeners : [],
 checkBusy : false,

 init : function (){
  Messages.checkMessages(500);
  window.addEventListener ('unload', Messages.onUnload, false);
 },

 marchAtTarget : function (){
  var t = Messages;
  t.checkMessages();
 },

 deleteQueue : [],
 deleteMessage : function (msgId){
  var t = Messages;
  if (t.deleteQueue.length == 0){
   t.deleteTimer = setTimeout (doit, Math.randRange(60000,90000));
  }
  t.deleteQueue.push (msgId);
  function doit (){
   var t = Messages;
   //logit ('DELETE MESSAGES:\n'+ inspect (t.deleteQueue, 5, 1));      
   MyAjax.messageDelete (t.deleteQueue, function (r){
    var t = Messages;
    t.deleteQueue = [];
   });
  }
 },

 onUnload : function (){
  var t = Messages;
  if (t.deleteQueue.length>0){
   MyAjax.messageDelete (t.deleteQueue);
  }
 },

 // check for battle reports
 checkMessages : function (maxWaitMillis){
  var t = Messages;
  if (t.battleReportListeners.length == 0){
   return;
  }
  if (maxWaitMillis == null)
  maxWaitMillis = 30000;
  RequestQueue.add ('checkMessages', doit, maxWaitMillis);      
  
  function doit (){
   MyAjax.messageList ('all', function (r){
    var t = Messages;
    if (r==null) return;
    //logit ('messageList:\n' + inspect (r, 7, 1));        
    for (var i=r.length-1; i >= 0; i--)
    {
     if (r[i].report_type=="BattleReport" && !r[i].read_at)
     {
      if (t.readList.indexOf(r[i].id) < 0){
       t.readList.push (r[i].id);
      }
     }
    }
    clearTimeout (t.fetchTimer);
    if (t.readList.length > 0){
     t.fetchTimer = setTimeout (t.fetchNext, Math.randRange(4000,8000));
    }
   });
  }
 },  

 fetchNext : function (){
  var t = Messages;
  var id = t.readList[0];
  if (!id){
   logit ('t.readList BAD MESSAGE ID:\n'+ inspect (t.readList, 8, 1));
   return;
  }    
  clearTimeout (t.fetchTimer);
  MyAjax.messageDetail (id, function (r){
   var t = Messages;
   if(r==null) return;
   t.readList.shift();
   t.gotBattleReport (r);
   if (t.readList.length > 0){
    t.fetchTimer = setTimeout (t.fetchNext, Math.randRange(4000,8000));
   }
  });
 },

 gotBattleReport : function (rpt){
  var t = Messages;
  if (DEBUG_MARCHES){
   WinLog.write ('Read Message: '+ rpt.report.location.terrain +' , '+ rpt.report.location.x +','+  rpt.report.location.y +' General: '+ rpt.report.attacker.general.id );    
  }
  for (var i=0; i < t.battleReportListeners.length; i++){
   t.battleReportListeners[i](rpt);
  }
 },

 addBattleReportListener : function (notify){
  var t = Messages;
  t.battleReportListeners.push (notify);
 },

 removeBattleReportListener : function (notify){
  var t = Messages;
  var i = t.battleReportListeners.indexOf (notify);
  if (i>=0){
   t.battleReportListeners.splice (i, 1);
  }
 }

}; // END Messages


RequestQueue = {
 que : {},
 add : function (id, func, maxWaitMillis){
  var t = RequestQueue;
  var now = serverTime();
  var maxWait = maxWaitMillis/1000;
  if (isNaN(maxWaitMillis)){
   maxWait = 1;
  }
  if (t.que[id]){
   if (now + maxWaitMillis >= t.que[id][2]){
    return;
   }
   clearTimeout(t.que[id][1]);  
  } 
  var timer = setTimeout (function(){myFunc(id)}, maxWait*1000);
  t.que[id] = [func, timer, now+maxWait];
  //dispQ ('RequestQueue.add id='+ id);  
  function myFunc(id){
   var t = RequestQueue;
   var func = t.que[id][0];
   delete t.que[id];
   //dispQ ('RequestQueue.doit id='+ id);  
   func();
  }
  
  // Translation
  function dispQ (str){
   var now = serverTime();
   var msg = str + ' (now='+ now +'):\n';
   for (var p in RequestQueue.que){
    msg += p +' : '+ RequestQueue.que[p][1] +' : '+ RequestQueue.que[p][2] +' ('+ (RequestQueue.que[p][2]-now) +')\n';
   }
   WinLog.write (msg);
  }   
 }, 

 isPending : function (id){
  var t = RequestQueue;
  return t.que[id]?true:false;
 }
}; //END RequestQueue



Seed = {
 cities   : [],   // cities
 cityIdx   : {},     // 'indicies'
 cityTs   : {},     // timestamps of last update
 jobs   : {},     // by city
 marches   : {},
 numMarches  : 0,
 generals  : {},
 requirements : {building:[], research:[], troop:[]},
 numGenerals  : 0,
 serverTimeOffset: 0,
 numCities  : 0,
 cityInit  : 0,
 tickTimer  : 0,
 
 init : function (callback) {
  var t = Seed;
 
  t.fetchPlayer(function (r) {
   if (r.ok) {
    verboseLog('Player data was Successfully requested from the server');
   }
   if (callback){
    callback(r);
   }
  });
  clearInterval(t.tickTimer);
  t.tickTimer = setInterval(t.tick, 1000);
 },
 
 fetchPlayer : function (callback) {
  var t = Seed, city;
  var params = {};
  params['user_id']  = USER_ID;
  params['_session_id'] = SESSION_ID;
  params['version']  = API_VERSION;
  params['timestamp']  = parseInt(serverTime());
  params['dragon_heart'] = DRAGON_HEART;  
  new MyAjax.RequestDOA ('player.json', params, function (r) {
   if (r.ok && !r.dat.errors) {
    if (r.dat.timestamp){
     t.serverTimeOffset = r.dat.timestamp - (new Date().getTime() / 1000);
    }
    
    t.player = r.dat; 
    
    t.numCities = 0;
    t.cityInit = 0;
    
    for (city in r.dat.cities) {
     t.numCities = t.numCities + 1;
    }

    try {
     for (city in r.dat.cities){
      t.fetchCity(r.dat.cities[city].id);
     }
    } catch (e) {
     r.ok = false;
     r.errmsg = e.toString();
    }
   }
   else if (r.ok && r.dat.errors) {
    r.ok = false;
    r.errmsg = r.dat.errors;
   }
   
   if (callback){
    callback(r);
   }
  });
 },
 
 fetchCity : function (cityId) {
  verboseLog('Attempting fetchCity ' + cityId);
  if(!cityId) return;
  var t = Seed;
  var params = {};
  params['user_id']  = USER_ID;
  params['dragon_heart'] = DRAGON_HEART;
  params['_session_id'] = SESSION_ID;
  params['timestamp']  = parseInt(serverTime());
  params['version']  = API_VERSION;
  new MyAjax.RequestDOA ('cities/'+ cityId +'.json', params, function (r) {
   if (r.ok && !r.dat.errors) {
    if (r.dat.timestamp){
     t.serverTimeOffset = r.dat.timestamp - (new Date().getTime() / 1000);
    }
    
    try {
     t.updateCity(r.dat.city);
     verboseLog('Updated coords for ' + r.dat.city.name + ' are ' + r.dat.city.x + '/' + r.dat.city.y);
    } catch (e) {
     r.ok = false;
     r.errmsg = e.toString();
    }
   }
   else if (r.ok && r.dat.errors) {
    r.ok = false;
    r.errmsg = r.dat.errors;
   }
  });
 },

 tick : function () {     // called once per second - to check for job completion
  var t = Seed;
  var now = parseInt(serverTime()) - 1;
  // delete expired marches ...
  for (var id in t.marches) {
   var march = t.marches[id];
   if ((march.run_at < now-30) || (march.status=='returning' && march.run_at < now-2)) {
    delete (t.marches[id]);
    --t.numMarches;
   }
  }
  // check for job completion
  for (var pcity in t.jobs) {
   for (var pjob in t.jobs[pcity]) {
    var job = t.jobs[pcity][pjob];
    if (job.run_at < (now - 300)) {
     if (job.done) {
      delete (t.jobs[pcity][pjob]);
     } else {
      //WinLog.write ('****** TIMER Seed.tick: RETAINING \'UNDONE\' JOB  (now='+ serverTime() +'):\n'+ inspect (job, 4, 1)); 
      //logit ('****** TIMER Seed.tick: RETAINING \'UNDONE\' JOB  (now='+ serverTime() +'):\n'+ inspect (job, 4, 1)); 
     }
    } else if (!job.done && job.run_at<now) {
     //WinLog.write ('TIMER Seed.tick: fetchCity JOB  (now='+ serverTime() +'):\n'+ inspect (job, 4, 1)); 
     job.done = true;
     delete (t.jobs[pcity][pjob]);
     var march = t.marches[job.march_id];
     // if (!march), march just finished (returned)          
     if (march && job.queue=='march' && march.status=='marching') {  // march just reached target
      if (DEBUG_MARCHES){
       WinLog.write ('MARCH at TARGET!');
      }
      Messages.marchAtTarget(march);
     }
     t.fetchCity (pcity);
     return;
    }
   }
  }
 },
 // TODO: fix march destination when city (shows as bog)
 updateCity : function (city) {
  var t = Seed;
  
  verboseLog('Updating City values: ' + city.name);
  
  var cityIdx = (city.type == 'Capital') ? 0 : (city.name.charAt(city.name.length-1));
  
  t.cities[cityIdx] = city;
  
  t.cityIdx[city.id] = cityIdx;
  
  t.cityTs[city.id] = serverTime();  
  
  if (cityIdx == 0) {
   // Update Seed.generals
   for (var i=0; i<city.generals.length; i++)
   {
    t.generals[city.generals[i].id] = city.generals[i];
   }
   t.numGenerals = city.generals.length;
   
   // Update Seed.marches
   t.numMarches = 0;
   for (var i=0; i < city.marches.length; i++)
   {
    var march = city.marches[i];
    
    if (march.general_id){
     t.generals[march.general_id].busy = true;
    }
    
    if (t.marches[march.id] != undefined){
     if (march.status == 'marching'){
      ++t.numMarches;
     }
    }
   
    t.checkMarchStatus (march);
   }
   verboseLog('Updated Seed.marches - total:' + city.marches.length + ' / marching: ' + t.numMarches + ' - returning: ' + (city.marches.length ? city.marches.length - t.numMarches - 1 : 0));
  }
  
  // Check Jobs
  for (var i=0; i < city.jobs.length; i++){
   t.checkAddJob (city.jobs[i]);
  }
  
  if (t.cityInit < t.numCities) {
   t.cityInit = t.cityInit + 1;
   verboseLog(city.name + ' Successfully initialised');
  } else {
   verboseLog(city.name + ' Successfully updated');
  }
 },

 // if fetchcity is pending, will notify when complete, else notifies right away...
 updateNotifyQueue : [],
 notifyOnUpdate : function (notify) {
  verboseLog('updateNotifyQueue');
  var t = Seed;
  if (!RequestQueue.isPending('fetchCity')) {
   notify();
   return;
  }
  t.updateNotifyQueue.push (notify);
 },

 checkMarchStatus : function (march){
  var t = Seed;

  var m = march.cloneProps();
  
  if (m.march_type == 'AttackMarch'){
   m.march_type = 'Attack';
  }
  else if (m.march_type == 'TransportMarch'){
   m.march_type = 'Transport';
  }
  
  if (m.status == 'retreating'){
   m.status = 'returning';
  }
  

  if (m.status == 'returning'){
   m.last_status = m.status;
  }
  
  m.target = m.destination_name  ? translate('City') +' '+ m.destination_name : translate(m.terrain_type);
  
  t.marches[m.id] = m;
 },

 checkAddJob : function (job){
  var t = Seed;
  var cityId = job.city_id;
  if (!job.run_at){
   WinLog.write ('checkAddJob job.run_at is null:\n'+ inspect (job, 5, 1));
   if (ALERT_ON_BAD_DATA){
    alert ('checkAddJob job.run_at is null');
   }
  }    
  
  if (!t.jobs[cityId]){
   t.jobs[cityId] = {};
  }
  if (job.queue == 'march'){
   if (!t.marches[job.march_id]){
    WinLog.write ('checkAddJob MISSING MARCH:\n'+ inspect (job, 5, 1) +'\n'+ inspect(t.marches, 5, 1));
    if (ALERT_ON_BAD_DATA){
     alert ('checkAddJob MISSING MARCH');
    }
    if (job.run_at < serverTime()){
     return;               // ?????? delete from Seed.jobs  ????
    }
   } 
   else {  
    t.marches[job.march_id].run_at = job.run_at;
    t.marches[job.march_id].duration = job.duration;
   }
  } 
  
  if (job.queue == 'units'){
  }

  if (t.jobs[cityId][job.id]){
   return;
  }
  job.run_at += 2;      
  t.jobs[cityId][job.id] = job.cloneProps();
 },

 checkIncomingData : function (r){
  var t = Seed;
  // check seed for missing building ...      
  for (var ij=0; ij < r.dat.city.jobs.length; ij++)
  {
   var job = r.dat.city.jobs[ij];
   if (job.queue == 'building'){
    var building = null;
    for (var im=0; im < r.dat.city.buildings.length; im++)
    {
     if (r.dat.city.buildings[im].id == job.city_building_id){
      building = r.dat.city.buildings[im];
      break;
     }
    }
    if (!building){
     WinLog.writeText ('*********************** MISSING BUILDING! ('+ job.city_building_id +') now='+ serverTime() +'\n' + inspect (job, 7, 1) +'\n'+ inspect (r, 12, 1));
     if (ALERT_ON_BAD_DATA){
      alert ('Danger Will Robinson! (missing building)');
     }
    }
   }
  }
  
  if (!r.dat.city.marches){
   return;
  }
  // check seed for missing march ...  
  for (var ij=0; ij < r.dat.city.jobs.length; ij++)
  {
   var job = r.dat.city.jobs[ij];
   if (job.march_id){
    if (t.findMarch(job.march_id, r.dat.city.marches) == null){
     WinLog.writeText ('*********************** MISSING MARCH, Job ID:'+ job.march_id +' (now='+ serverTime() +')\n'+ inspect (job, 7, 1) +'\n'
     + inspect (r, 12, 1));
     if (ALERT_ON_BAD_DATA){
      alert ('Danger Will Robinson! (missing march)');
     }
    }
   }
  }   
  // check seed for missing march job ...  
  for (var im=0; im < r.dat.city.marches.length; im++)
  {
   var march = r.dat.city.marches[im];
   var job = null;
   for (var ij=0; ij < r.dat.city.jobs.length; ij++)
   {
    if (r.dat.city.jobs[ij].march_id == march.id){
     job = r.dat.city.jobs[ij];
     break;
    }
   }
   if (job==null){
    WinLog.writeText ('*********************** MISSING JOB FOR MARCH!  marchId:'+ march.id +'\n'+ inspect (r, 11, 1));
    if (ALERT_ON_BAD_DATA){
     alert ('MISSING JOB FOR MARCH!');
    }
   }
  }
 },

 findMarch : function (march_id, marches){
  for (var mid=0; mid < marches.length; mid++){
   if (marches[mid].id == march_id){
    return marches[mid];
   }
  }
  return null;
 }

}; // END Seed




Translation = {
 loaded : false,
 object : {},
 availableLangs : {
  da:true, 
  de:true, 
  en:true, 
  es:true, 
  fr:true, 
  gr:false, 
  id:false, 
  it:true, 
  nl:true, 
  pl:true, 
  pt:false, 
  ru:false,
  sv:true, 
  tr:true
 },
 
 /* WARNING: DON'T CHANGE THIS ORDER */
 _section : [
  'items',
  'common',
  'buildings',
  'messages',
  'dialogs',
  'levels',
  'troops',
  'map',
  'research'
 ],
 
 init : function (notify) {
  var t = Translation;
  t.fetchLocale(function (r) {
   if (r.ok) {
    verboseLog('Locale data was Successfully requested from the sever');
    t.loaded = true;
    t.fixResults();
   }
   if (notify){
    notify(r);
   }
  });
 },

 fetchLocale : function (notify) {
  var t = Translation;
  new MyAjax.RequestDOA ('locales/' + (t.availableLangs[LANG_CODE]?LANG_CODE:'en') + '.xml', {'_swf_session_id':SESSION_ID}, function (r) {
   if (r.ok) {
    try {
     t.parseXML(r.dat);
     
    } catch (e) {
     r.ok = false;
     r.errmsg = e.toString();
    }
   } 
   else if (r.errmsg.indexOf('404') !== -1) {
    new MyAjax.RequestDOA('locales/en.xml', {'%5Fswf%5Fsession%5Fid':SESSION_ID}, function (r) {
     if (r.ok) {
      try {
      
       t.parseXML(r.dat);
       
      } catch (e) {
       r.ok = false;
       r.errmsg = e.toString();
      }
     }
     if (notify) {
      notify(r);
     }
    });
   }
   if (notify){
    notify(r);
   }
  });
 },
 
 parseXML : function(stringXML){
  var t = Translation;
  var fragment = [];
 
  fragment.push('<?xml version="1.0" encoding="UTF-8"?>');
  fragment.push('<translations>');
  for (i=0; i < t._section.length; i++)
  {
   var start = stringXML.indexOf('<'+t._section[i]+'>');
   var end = stringXML.indexOf('</'+t._section[i]+'>') + t._section[i].length + 3;
   fragment.push(stringXML.substring(start, end));
   stringXML = stringXML.substring(1, start) + stringXML.substring(end);
  }
  fragment.push('</translations>');

  var XMLObj = new XML.ObjTree();
  t.object = XMLObj.parseXML( fragment.join('').replace(/\n/g,'') );

  if (t.object.translations){
   t.object = t.object.translations;
  } else {
   verboseLog('<b>ERROR</b> in the XML file structure: <b><translations></b> element not found!');
  }
 },
 
 fixResults : function(){
  var t = Translation.object;
  
  // Convert Objects in flat Object
  // ex: 
  //     root-key : { title: '', content-1: { name: '', desc: ''} }
  //
  // become
  //
  //     root-key-title: '' & root-key-content-1-name : '' & root-key-content-1-desc : ''
  // 
  function objectToFlat(obj){
   var r={};
   for(var key in obj){
    if(typeof obj[key] == 'object'){
     for (var subkey in obj[key]){
      if(typeof (obj[key])[subkey] == 'object'){
       for (var subsubkey in (obj[key])[subkey]){
        if(subsubkey == 'title' || subsubkey == 'name'){
         r[key+'-'+subkey] = ((obj[key])[subkey])[subsubkey];
        } else {
         r[key+'-'+subkey+'-'+subsubkey] = ((obj[key])[subkey])[subsubkey];
        }
       }
      } else {
       if(subkey == 'title' || subkey == 'name'){
        r[key] = (obj[key])[subkey];
       } else {
        r[key+'-'+subkey] = (obj[key])[subkey];
       }
      }
     }
    } else {
     r[key] = obj[key];
    }
   }
   return r;
  }
  
  var section = ['dialogs','messages'];//,'errors','confirmations'
  for (var i=0; i < section.length; i++) {
   t[section[i]] = objectToFlat( t[section[i]] );
  }
   
  
  
  t.common.information = t.common.info;
  t.common.omit = t.common.skip;
  t.common['spy-on'] = t.common.spy;
  t.dialogs.researching = t.dialogs.research;
  
  t.common['enter-coords'] = t.dialogs['attack-screen-enter-coords'];
  t.common['battle-report'] = t.messages['battle-report-title'];
  t.common['auto-collection-of-resources'] = t.dialogs['boost-collect-day'].replace(/:/,'');
  
  t.common.levels = findSimilarWord(t.common.level, t.messages['spy-tip-prefix']);
  
  delete t.common.error;
  delete t.common.home;
  delete t.common.info;
  delete t.common['ranged-attack'];
  delete t.common.skip;
  delete t.common.spy;
  delete t.messages.date;
  delete t.messages.fought;
  delete t.messages.subject;
  delete t.messages.to;
  delete t.dialogs.research;
  delete t.dialogs.spy;
  delete t.dialogs.unavailable;
  delete t.dialogs.upkeep;
 },
 
 _normalize : function (str){
  return (str||'').toLowerCase().replace(/ /g,'-');
 },
 
 getContent : function(section,key,subkey){
  key = Translation._normalize(key);
  if(Translation.object[section] != undefined) {
   if( (Translation.object[section])[key] != undefined ) {
    return subkey ? ((Translation.object[section])[key])[subkey] : (Translation.object[section])[key];
   }
  }
  return false;
 },
 
 buildings : function(key,subkey){
  subkey = subkey != undefined ? subkey : 'name';
  return Translation.getContent('buildings',key,subkey);
 },
 
 common : function(key){
  return Translation.getContent('common',key);
 },
 
 items : function(key,subkey){
  subkey = subkey != undefined ? subkey : 'name';
  return Translation.getContent('items',key,subkey);
 },
 
 dialogs : function(key){
  return Translation.getContent('dialogs',key);
 },
 
 levels : function(key){
  return Translation.getContent('levels',key,'title');
 },
 
 map : function(key,subkey){
  subkey = subkey != undefined ? subkey : 'name';
  return Translation.getContent('map',key,subkey);
 },
 
 messages : function(key){
  return Translation.getContent('messages',key);
 },
 
 troops :  function(key,subkey){
  subkey = subkey != undefined ? subkey : 'name';
  return Translation.getContent('troops',key,subkey);
 },
 
 research :  function(key,subkey){
  subkey = subkey != undefined ? subkey : 'name';
  return Translation.getContent('research',key,subkey);
 }
 
}; // END Translation

// Provide language translation services based on the browswer language
translate = function( text ) {
 //if (text==undefined) return;
 
 if ( LANG_OBJECT[text] != undefined ) {
  return LANG_OBJECT[text];
 }
 else if (Translation.loaded){
  var newStr;
  for (var i=0; i < Translation._section.length; i++){
   newStr = Translation[Translation._section[i]](text);
   if (newStr){
    return newStr;
   }
  }
  
  if(IS_NOT_NATIVE_LANG && ToTranslate[text] == undefined) {
   ToTranslate[text] = 1;
   if(Tabs.Log){
    logit( '( Translate ) -> ' + text );
   }
  }
 }
 
 return text;
};

VerboseLog = {
 init : function () {
  var t = VerboseLog;
  t.setEnable(Data.options.verboseLog.enabled);
 },
 
 setEnable : function (onOff) {
  var t = VerboseLog;
  Data.options.verboseLog.enabled = onOff;
 }
};// END VerboseLog

//****************
// Functions
//****************


function objAddTo (o, name, val){
 if (!o[name]){
  o[name] = val;
 } else {
  o[name] += val;
 }
}

function getGeneralsList (cityIdx){
 var ret = {};
 var gens = Seed.cities[cityIdx].generals;
 for (var i=0; i < gens.length; i++){
  ret[gens[i].id] = gens[i].name +' ('+ gens[i].rank +')';
 }
 return ret;
}

function getTroopNumbers (cityIdx, unit_type) {
 var city = (typeof cityIdx == 'number') ? Seed.cities[cityIdx] : cityIdx;
 var incity = city.units[unit_type] ? city.units[unit_type] : 0;
 var marches = 0;
 for (var id in Seed.marches){
  for (var name in Seed.marches[id].units){
   if (unit_type == name){
    marches += Seed.marches[id].units[name];
   }
  }
 }
 return {incity:incity, marches:marches, total:incity+marches};
}

function checkAvailableTroops (cityIdx, level){
 var troops = Data.options.objAttack.troops[level];
 var total = 0;
 var obj = {};
 for (var unit_type in troops){
  if (troops[unit_type] > 0){
   total += troops[unit_type];
   if (Seed.cities[cityIdx].units[unit_type] < troops[unit_type]){
    obj.total = total;
    obj.error = translate('Not enough') +' '+ translate(unit_type);
    return obj
   }
  }
 }
 obj.total = total;
 if (total <= 0){
  obj.error = translate('No Troops Defined');
  return obj;
 }
 return obj;
}

function getAvailableGeneral (){
 for (var p in Seed.generals){
  if (!Seed.generals[p].busy){
   return Seed.generals[p];
  }
 }
 return null;
}

function getMusterPointSlots (cityIdx){
 var lvl = Buildings.getLevel (cityIdx, kMusterPoint);
 if (!lvl){
  return 0;
 }
 return lvl - Seed.numMarches;
}

function getMusterPointLevel (cityIdx){
 var lvl = Buildings.getLevel (cityIdx, kMusterPoint);
 return (!lvl) ? 0 : lvl;
}

function getBuildingJob (cityIdx){
 var cid = Seed.cities[cityIdx].id;
 for (var p in Seed.jobs[cid]){
  var job = Seed.jobs[cid][p];
  if (job.queue == 'building'){
   return ({job:job, building:Buildings.getById(cityIdx, job.city_building_id)});
  }
 }
 return null;
}

function getResearchJob (cityIdx){
 var cid = Seed.cities[cityIdx].id;
 for (var p in Seed.jobs[cid]){
  var job = Seed.jobs[cid][p];
  if (job.queue == 'research'){
   return (job);
  }
 }
 return null;
}

function getBuildingById (cityIdx, bId){
 var b = Seed.cities[cityIdx].buildings;
 for (var i=0; i<b.length;i++){
  if (b[i].id == bId){
   return b[i].type;
  }
 }
 
 return '';
}

function MarchTracker (){
 var marches = {};

 function MarchTracker (){
 }

 this.setReportDelete = function (onOff){
 }
 this.setTroopLossListener = function (listener){
 } 
}

function deleteResearchJob(job){
 var cid = Seed.cities[0].id;
 var jobs = Seed.jobs[cid];
 for (var p in jobs){
  if (jobs[p] == job){
   delete jobs[p];
  }
 } 
}

function deleteBuildJob(cityIdx, job){
 var cid = Seed.cities[cityIdx].id;
 var jobs = Seed.jobs[cid];
 for (var p in jobs){
  if (jobs[p] == job){
   delete jobs[p];
  }
 } 
}

function getBuildJob (cityIdx){
 var cid = Seed.cities[cityIdx].id;
 var jobs = Seed.jobs[cid];
 for (var p in jobs){
  //console.log(jobs[p].queue);
  if (jobs[p].queue == 'building'){
   return jobs[p];
  }
 }
 return null;
}

function getTrainJob (cityIdx){
 var cid = Seed.cities[cityIdx].id;
 var jobs = Seed.jobs[cid];
 for (var p in jobs){
  if (jobs[p].queue == 'units'){
   return jobs[p];
  }
 }
 return null;
}

function trainTable (myId){
 var html = '<table class=' + UID['table'] + '>';
 var now = serverTime();
 var mtClass = '' + UID['row_marchMine'];
 for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++)
 {
  var jobs = Seed.cities[cityIdx].jobs;
  for (var j=0; j < jobs.length; j++)
  {
   var time = jobs[j].run_at - now;
   if (time < 0){
    time = '?';
   } else if (isNaN (time)){
    time = '---';
   } else {
    time = timeFormat(time, true);
   }
   if (jobs[j].queue == 'units' && jobs[j].unit_type){
    html += '<tr class='+ mtClass +'><td><b>'+ translate('Training') +':</b>&nbsp;</td><td align=right>'+ jobs[j].quantity +'&nbsp;</td><td>&nbsp;'+ translate(jobs[j].unit_type) +'&nbsp;</td><td>&nbsp;'+ time +'</td></tr>';   
   }
  }
 }
 return html + '</table>';
}





//******************************** Info Tab *****************************
Tabs.Info = {
 tabOrder : INFO_TAB_ORDER,
 tabLabel : 'Info',
 tabDisabled : !INFO_TAB_ENABLE,
 container : null,
 timer  : null,
 units_type : [kPorter,kConscript,kSpy,kHalberdsman,kMinotaur,kLongbowman,kSwiftStrikeDragon,kBattleDragon,kArmoredTransport,kGiant,kFireMirror,kPackDragon,kAquaTroop,kStoneTroop,kFireTroop,kWindTroop],
 dragonTypes : [kGreatDragon,kWaterDragon,kStoneDragon,kFireDragon,kWindDragon],

 init : function (div){
  var t = Tabs.Info;
  t.container = div;
  
  var html = [
   '<div id=' + setUID('tabInfo_Title') + ' class=' + UID['title'] + '>' + translate('Info') + '</div>'
  ,'<table width=100%>'
  ,'<tr>'
  ,' <td><input id=' + setUID('tabInfo_Refresh') + ' type=button value=' + translate('Refresh') + '></input></td>'
  ,' <td align=right><span id=' + setUID('tabInfo_Time') + '></span></td>'
  ,'</tr></table>'
  ,'<div id=' + setUID('tabInfo_Content') + ' class="' + UID['scrollable'] + '"></div>'
  ];
  
  div.innerHTML = html.join('');
  
  $J('#'+UID['tabInfo_Content']).css('height','640px');
  
  // Event Listeners
  $J('#'+UID['tabInfo_Refresh']).click(t.refresh);
  
  t.showStuff();
 },

 show : function (){
  var t = Tabs.Info;
  t.timer = setInterval (t.showStuff, 1000);
 },
 hide : function (){
  var t = Tabs.Info;
  clearInterval (t.timer);
 },

 showStuff : function (){
  var t = Tabs.Info;
  
  var city = Seed.cities[0];

  function cityTitle (cityIdx){
   var city = Seed.cities[cityIdx];
   // Outposts are always defending (until further notice)
   var wall_status = '';
   var alliance_name = (Seed.player.alliance) ? Seed.player.alliance.name : '';
   if (cityIdx == 0){
    if (Seed.cities[cityIdx].defended) {
     wall_status = '<span class=' + UID['defending'] + '>' + translate('Defend').toUpperCase() + '</span>';
    } else {
     wall_status = '<span class=' + UID['hiding']    + '>' + translate('Hiding').toUpperCase() + '</span>';
    }
   } else {
    wall_status = '<span class='+ UID['defending'] +'>'+ translate('Defend').toUpperCase() +'</span>';
   }
   
   return [
    '<div class=' + UID['subtitle'] + '>'
   ,' <table class=' + UID['table'] + '>'
   ,' <tr>'
   ,'  <td align=left>' + city.name + '</td>'
   ,'  <td align=center>' + city.x + ',' + city.y + '</td>'
   ,'  <td align=center width=200px>'
   ,'   <font color=yellow>' + alliance_name + '</font>'
   ,'  </td>'
   ,'  <td width=80px align=right>' + wall_status + '</td>'
   ,' </tr>'
   ,' </table>'
   ,'</div>'
   ].join('');
  }
  
  var html = [ cityTitle(0)
  ,'<table style="margin-top:3px" width=100%>'
  ,' <tr class=' + UID['row_headers'] + ' align=center>'
  ,'  <td>' + translate('Armed Forces') + '</td>'
  ,'  <td>' + translate('My Generals') + '</td>'
  ,' </tr>'
  ,' <tr valign=top align=center>'
  ,'  <td width=50% style="border-right: 1px solid;">'
  ];
  
  // Troops
  html = html.concat([
   '  <table class=' + UID['table'] + '>'
  ]);
  
  for (var i=0; i < t.units_type.length; i++){
   var numTroops = getTroopNumbers(city, t.units_type[i]);
   
   html = html.concat([
    '  <tr>'
   ,'   <td class=right>' + translate(t.units_type[i]) + ':</td>'
   ,'   <td align=right>' + numTroops.incity + '</td>'
   ,'   <td align=right>' + (numTroops.marches ? '&nbsp;+&nbsp;<b>(' + numTroops.marches + ')</b>' : '') + '</td>'
   ,'  </tr>'
   ]);
  }
  
  html = html.concat([
  '   </table>'
  +'  </td>'
  +'  <td width=50% style=" padding-left:7px">'
  ]);
  
  // Generals
  html = html.concat([
  '   <table class=' + UID['table'] + '>'
  ]);
  
  var loc = '';
  for (var i=0; i < city.generals.length; i++)
  {
   if (Seed.numMarches){
    for (var id in Seed.marches) {
     // The general object will be null if the march is a transport
     if (Seed.marches[id].march_type != "Transport") {
      try {
       if (city.generals[i].name == Seed.marches[id].general.first_name)
       loc = Seed.marches[id].x + ',' + Seed.marches[id].y;
      }
      catch (e) {
       verboseLog('<b>Error</b>: general first_name not available' + e.name + ' ' + e.message);
      }
     }
    }
   }
   
   html = html.concat([
    '  <tr>'
   ,'   <td align=right>' + city.generals[i].name  + ' (' + city.generals[i].rank + ')</td>'
   ,'   <td width=75%>' + (city.generals[i].busy ? '<span class=jewel>[' + loc + ']</span>' :'') + '</td>'
   ,'  </tr>'
   ]);
  }
  
  html = html.concat([
   '   </table>'
  ,'  </td>'
  ,'  </tr>'
  ,'</table>'
  ,'<br>'
  ,'<table class=' + UID['table'] + '>'
  ,' <tr>'
  ,'  <td class=left>' + translate('Marching') + ': </td>'
  ,'  <td>' + Seed.numMarches + '</td>'
  ,' </tr>'
  ,'</table>'
  ]);
  
  // Marches, building, research, training
  $J("#"+UID['tabInfo_Content']).html(html.join(''));
  
  var now = new Date();  
  now.setTime(now.getTime() + (now.getTimezoneOffset()*60000));
  $J("#"+UID['tabInfo_Time']).html('<b>' + now.toTimeString().substring (0,8) +'</b> UTC');

  
 },

 refresh : function (){
  logit('fetchPlayer from Tab.Info refresh');
  var t = Tabs.Info;
  Seed.fetchPlayer (t.showStuff());  
 }
} // END Tabs.Info


//******************************** Waves Tab *****************************
Tabs.Waves = {
 tabOrder : WAVE_TAB_ORDER,
 tabLabel : 'Wave',
 tabDisabled : !WAVE_TAB_ENABLE,
 container : null,
 units_type : [kSpy, kArmoredTransport, kPackDragon, kLongbowman, kSwiftStrikeDragon, kBattleDragon, kGiant, kFireMirror, kAquaTroop, kStoneTroop, kFireTroop, kWindTroop],
 dragonTypes : [kGreatDragon,kWaterDragon,kStoneDragon,kFireDragon,kWindDragon],
 enabled  : false,
 attackTimer : null,
 marchTimer : null,
 attackErrors: 0,
 currentWave : 1,

 init : function (div) {
  var t = Tabs.Waves;

  Data.init({
   waves: {
    enabled   : false,
    iterationMin : 30,
    iterationMax : 60,
    stopOnLoss  : true,
    deleteReports : false,
    target : {
     x : Data.map.x,
     y : Data.map.y,
     type: null,
     level: 0,
     stats: {
      numAttacks : 0,
      spoils  : {}
     },
     troopsWave:{},
     dragonsWave:{}
    },
    runTime : 0
   }
  });

  
  t.container = div;
  
  var html = [
   '<div class=' + UID['title'] + '>' + translate('Wave') + '</div>'
  ,'<div id=' + setUID('tabWave_Status') + ' class=' + UID['status_ticker'] + ' style="margin-bottom:5px !important">'
  ,' <center><input id=' + setUID('tabWave_OnOff') + ' type=button value="OnOff" /></center>'
  ,' <div class=' + UID['status_report'] + ' style="margin-top:5px;height:140px; max-height:140px; overflow-y:auto;">'
  ,'  <table id=' + setUID('tabWave_Marches') + ' class=' + UID['table'] + '></table>'
  ,' </div>'
  ,' <div id=' + setUID('tabWave_Feedback') + ' class='+ UID['status_feedback'] +'></div>'
  ,'</div>'
  ,'<div class=' + UID['content'] + '>'
  ,' <div>'
  ,'  <b>'+ translate('Enter Coords') +':&nbsp;</b>&nbsp;'
  ,'  <b>X:</b> <input id=' + setUID('tabWave_CoordsX') + ' size=3 maxlength=3 type=text value="' + Data.waves.target.x + '" /> '
  ,'  <b>Y:</b> <input id=' + setUID('tabWave_CoordsY') + ' size=3 maxlength=3 type=text value="' + Data.waves.target.y + '" /> '
  ,'  &nbsp <b>'+ translate('Distance') + ':</b> <span id=' + setUID('tabWave_Distance') + '></span><BR>'
  ,'  <div class=' + UID['status_ticker'] + ' style="height:auto !important;margin:5px 10px !important;">'
  ,'   <center><span id=' + setUID('tabWave_Tile') + '></span></center>'
  ,'  </div>'
  ,' </div>'
  ,'  <div>'
  ,'  <center>'
  ,' <table id=' + setUID('tabWave_Troops') + ' class=' + UID['table'] + '>'
  ,'  <tr align=center class=' + UID['row_headers'] + '>'
  ,'   <td colspan=8>' + translate('Troops for Wave Attack') + ':&nbsp;</td>'
  ,'  </tr>'
  ,' </table>'
  ,'  </center>'
  ,' </div>'
  ,' <br>'
  ,'  <div>'
  ,'  <center>'
  ,' <table id=' + setUID('tabWave_Dragons') + ' class=' + UID['table'] + '>'
  ,'  <tr align=center class=' + UID['row_headers'] + '>'
  ,'   <td colspan=8>' + translate('Send Dragon every certain number of waves') + ':&nbsp;</td>'
  ,'  </tr>'
  ,' </table>'
  ,'  </center>'
  ,' </div>'
  ,' <br>'
  ,' <table class=' + UID['table'] + '>'
  ,'  <tr>'
  ,'   <td class=left> ' + translate('Delete') + ' ' + translate('Battle Report') + ':&nbsp;</td>'
  ,'   <td><input id=' + setUID('tabWave_DelReports') + ' type=checkbox ' + (Data.waves.deleteReports?'CHECKED':'') + ' /></td>'
  ,'  </tr><tr>'
  ,'   <td class=left>' + translate('Stop if any troops lost') + ':&nbsp;</td>'
  ,'   <td><input id=' + setUID('tabWave_StopOnLoss') + ' type=checkbox ' + (Data.waves.stopOnLoss?'CHECKED':'') + ' /></td>'
  ,'  </tr><tr>'
  ,'   <td class=left>' + translate('Delay Between Attacks') + ':&nbsp;</td>'
  ,'   <td>'
  ,'    <input id=' + setUID('tabWave_DelayMin') + ' type=text size=1 maxlength=4 value="' + Data.waves.iterationMin + '" />'
  ,'     to <span id=' + setUID('tabWave_DelayMax') + '>' + Data.waves.iterationMax + '</span>&nbsp;' + translate('Seconds')
  ,'   </td>'
  ,'  </tr>'
  ,' </table>'
  ,'</div>'
  ,'<div class=' + UID['status_ticker'] + ' style="margin-top:10px !important">'
  ,' <center>'
  ,'  <input id=' + setUID('tabWave_ResetStats') + ' type=button value="' + translate('Delete') + ' ' + translate('Statistics') + '" />'
  ,' </center>'
  ,' <div id=' + setUID('tabWave_Stats') + '  style="height:100px; max-height:100px; overflow-y:auto"></div>'
  ,' <hr class=thin>'
  ,' <div id=' + setUID('tabWave_CurSpoil') + '> &nbsp; </div>'
  ,'</div>'
  ];
  
  t.container.innerHTML = html.join('');
  
  
  // Event Listeners
  
  $J('#'+UID['tabWave_OnOff']).click(function(){
   t.setWaveEnable(!Data.waves.enabled);
  });
  $J('#'+UID['tabWave_CoordsX']).change(t.eventCoords);
  $J('#'+UID['tabWave_CoordsY']).change(t.eventCoords);
  $J('#'+UID['tabWave_ResetStats']).click (t.resetStats);
  $J('#'+UID['tabWave_DelReports']).click (function(e){
   Data.waves.deleteReports=e.target.checked;
  });
  $J('#'+UID['tabWave_StopOnLoss']).click ('click', function(e){
   Data.waves.stopOnLoss=e.target.checked;
  }, false);

  $J('#'+UID['tabWave_DelayMin']).change (delayChanged);

  troopTable ($id(UID['tabWave_Troops']), 1, 'AW', t.eventTroops);

  dragonsTable ($id(UID['tabWave_Dragons']), 1, t.eventDragons);

  window.addEventListener('unload', t.onUnload, false);
  
  t.setWaveEnable (false);
  t.marchTick();
  t.eventCoords();
  t.dispStats();
  Messages.addBattleReportListener(t.gotBattleReport);

  function troopTable (tab, rownum, prefix, listener) {
   var t = Tabs.Waves;
   var row =[];
   row.push(tab.insertRow(rownum));
   row.push(tab.insertRow(rownum+1));
   row.push(tab.insertRow(rownum+2));
   row.push(tab.insertRow(rownum+3));
   //row[0].align='center';
   
   var val, r=0, c=0;
   for (var i=0; i < t.units_type.length; i++)
   {
    /*
    if (getTroopNumbers(Seed.cities[0], t.units_type[i]).total < 1) {
     continue;
    }
    */
    if (i == 6) {
     r = r + 2;
     c = 0;
    }
    var label = row[r].insertCell(c);
    label.innerHTML = translate('~'+t.units_type[i]);
    label.style.width = '45px';
    
    var input = document.createElement ('input');
    input.type = 'text';
    input.size = '1';
    input.style.width = '40px';
    input.title = translate(t.units_type[i]);
    
    if (i < 3) {
     input.style.border = '1px solid grey';
    } else if (i < 6) {
     input.style.border = '1px solid green';
    } else {
     input.style.border = '1px solid blue';
    }
    
    input.maxlength = '6'; // Allow 100,000 troops to be sent
    
    if (prefix=='AW'){
     if (Data.waves.target.troopsWave[t.units_type[i]] == undefined){
      Data.waves.target.troopsWave[t.units_type[i]] = 0;
     }
     val = Data.waves.target.troopsWave[t.units_type[i]];
    }
    
    if (!val){ val = 0; }
    
    input.value = val;
    input.name = prefix +'_'+ i;
    $J(input).change (listener);
    
    row[r+1].insertCell(c).appendChild (input);
    
    c = c + 1;
    
   }
   return tab;
  }
  
  function dragonsTable (tab, rownum, listener) {
   var t = Tabs.Waves;
   var row =[];
   row.push(tab.insertRow(rownum));
   //row.push(tab.insertRow(rownum+1));
   
   var val;
   for (var i=0; i < t.dragonTypes.length; i++)
   {
    
    var cell = row[0].insertCell(i);
    cell.style.verticalAlign = 'middle';
    cell.style.paddingRight = '10px';
    
    var label = document.createElement ('span');
    label.innerHTML = translate(t.dragonTypes[i]).replace(/\s/,'<br>');
    label.style.display = 'inline-block';
    label.style.width = '45px';
    label.style.marginRight = '5px';
    
    var input = document.createElement ('input');
    input.type = 'text';
    input.size = '1';
    input.style.padding = '0px';
    input.style.width = '16px';
    input.style.height = '16px';
    input.style.fontSize = '14px';
    input.style.textAlign = 'center';
    input.title = translate(t.dragonTypes[i]);
    
   
    input.maxlength = '1'; 
    
    if (Data.waves.target.dragonsWave[t.dragonTypes[i]] == undefined){
     Data.waves.target.dragonsWave[t.dragonTypes[i]] = 0;
    }
    val = Data.waves.target.dragonsWave[t.dragonTypes[i]];
    
    if (!val){ val = 0; }
    
    input.value = val;
    input.name = 'DW_'+ i;
    $J(input).change (listener);

    cell.appendChild(label);
    cell.appendChild(input);
    
   }
   return tab;
  }
  
  function delayChanged (event){
   var min = parseIntZero(event.target.value);
   var max = parseInt(min * 2);
   if (min < 30 || min > 3600){
    if(min < 30) min = 30;
    else min = 3600;
    // error dialog, etc ...
    event.target.style.backgroundColor = 'red';
    return;
   }
   $id(UID['tabWave_DelayMax']).innerHTML = max;
   event.target.style.backgroundColor = '';
   Data.waves.iterationMin = min;
   Data.waves.iterationMax = max;
  }
 },

 curRunStart : 0,
 
 gotBattleReport : function (rpt){
  var t = Tabs.Waves;
  
  if (rpt.report.location.x == Data.waves.target.x && 
   rpt.report.location.y == Data.waves.target.y
   ){
    ++Data.waves.target.stats.numAttacks;
    
    var march_id = null;
    for (var id in Data.marches.waves )
    {
     var march = Data.marches.waves[id];
     
     if (march.x == rpt.report.location.x && 
      march.y == rpt.report.location.y &&
      march.general.id == rpt.report.attacker.general.id
      ){  // TODO: time and troops check here
       march_id = id;
       break;
     }
    }
    if (march_id) Marches.remove (march_id, 'waves');
    
    for (var i=0; i < rpt.report.spoils.items.length; i++){
     if ( !Data.waves.target.stats.spoils[rpt.report.spoils.items[i]] )
     {
      Data.waves.target.stats.spoils[rpt.report.spoils.items[i]] = 1;
     }
     else {
      ++Data.waves.target.stats.spoils[rpt.report.spoils.items[i]];
     }
     $id(UID['tabWave_CurSpoil']).innerHTML = new Date().toTimeString().substring (0,8) +': '+ translate('Got') + ' '+ translate(rpt.report.spoils.items[i]);
    }
    t.dispStats();
    
    if (Data.waves.stopOnLoss)
    {
     for (var p in rpt.report.attacker.units)
     {
      if (rpt.report.attacker.units[p][0] != rpt.report.attacker.units[p][1])
      {
       var ts = new Date(rpt.report_notification.created_at * 1000).myString();
       t.setWaveEnable (false);
       t.dispFeedback (translate('Troops lost') + '! (' + ts +')');
       actionLog (translate('Wave')+': '+translate('Troops lost')+'! ('+ ts +')');
       return;
      }
     }
    }
    if (Data.waves.deleteReports && rpt.report.attacker.name == Seed.player.name){
     Messages.deleteMessage(rpt.report_notification.id);
    }
  }
 },

 resetStats : function (){
  var t = Tabs.Waves;
  var now = serverTime();
  t.curRunStart = now;

  Data.waves.runTime = 0;
  Data.waves.target.stats = {numAttacks:0, spoils:{}};
  t.dispStats();
 },

 dispStats : function (){
  var t = Tabs.Waves;
  var runTime = Data.waves.runTime;
  
  if (Data.waves.enabled){
   runTime += (serverTime()-t.curRunStart);
  }
  
  var html = [
   '<table class=' + UID['table'] + ' width=100%>'
  ,' <tr>'
  ,'  <td class=left>' + translate('Run Time') + ': </td>'
  ,'  <td width=90%>' + timeFormat(runTime, true) + '</td>'
  ,' </tr><tr>'
  ,'  <td class=left>' + translate('Attacks') + ': </td>'
  ,'  <td>' + Data.waves.target.stats.numAttacks + '</td>'
  ,' </tr><tr>'
  ,'  <td colspan=2>'
  ,'   <hr class=thin>'
  ,'  </td>'
  ,' </tr>'
  ];
  
  
  for (var item in Data.waves.target.stats.spoils)
  {
   var num = Data.waves.target.stats.spoils[item];
   var perHour = num / (runTime/3600);
   html = html.concat([
    '<tr>'
   + '  <td class=left>' + translate(item) + ':</td>'
   + '  <td>' + num + ' (' + perHour.toFixed(2) + '&nbsp;' + translate('per hour') + ')</td>'
   + '</tr>'
   ]);
  }
  
  $id(UID['tabWave_Stats']).innerHTML = html.concat(['</table>']).join('');
 },

 dispFeedback : function (msg){
  if (msg && msg!='')
  msg = new Date().toTimeString().substring (0,8) +' '+ msg;
  $id(UID['tabWave_Feedback']).innerHTML = msg;
 },

 eventTroops : function (event){
  var t = Tabs.Waves;
  var args = event.target.name.split ('_');
  if (args[0] == 'AW'){
   var unit_type = t.units_type[args[1]];
   Data.waves.target.troopsWave[unit_type] = event.target.value;
  }
 },
 
 eventDragons : function (event){
  var t = Tabs.Waves;
  var args = event.target.name.split ('_');
  if (args[0] == 'DW'){
   var dragonType = t.dragonTypes[args[1]];
   Data.waves.target.dragonsWave[dragonType] = event.target.value;
  }
 },

 setWaveEnable : function (onOff){
  var t = Tabs.Waves;
  var but = $id(UID['tabWave_OnOff']);
  clearTimeout (t.attackTimer);
  Data.waves.enabled = onOff;
  if (onOff){
   but.value = translate('Attacking').toUpperCase();
   but.className = UID['btn_on'];
   t.curRunStart = serverTime();
   if (!Data.options.objAttack.enabled){
    Data.marches.countForLimit = 1;
    Data.marches.startAt = serverTime();
   }
   t.waveAttackTick();
  } 
  else {
   but.value = translate('Disabled').toUpperCase();
   but.className = UID['btn_off'];
   if (t.curRunStart != 0){
    Data.waves.runTime += (serverTime()-t.curRunStart);
   }
  }
 },

 onUnload : function (){
  var t = Tabs.Waves;
  if (Data.waves.enabled && t.curRunStart!=0){
   Data.waves.runTime += (serverTime()-t.curRunStart);
  }
 },


 waveAttackTick : function (){
  var t = Tabs.Waves;
  var now = serverTime();
  var targetMsg='', retryDelay, availableGeneral, marching = 0, totalMarches=0;
  var waveUnits;
  
  clearTimeout (t.attackTimer);
  
  // Don't do anything if wave attacks are not enabled
  if (!Data.waves.enabled){
   return;
  }    
  
  
  var minTime = 700000;
  var maxTime = 0;
  for (id in Seed.marches){
   ++totalMarches;
   if (Seed.marches[id].status == 'marching'){
    ++marching;
   }
   var restTime = ( Seed.marches[id].run_at - parseInt(serverTime()) ) * (Seed.marches[id].status=='marching'?2:1);
   minTime = minTime < restTime ? minTime : restTime;
   maxTime = maxTime > restTime ? maxTime : restTime;
  }
  
  retryDelay = totalMarches ? minTime*1000 + Math.randRange(2000,5000) : Math.randRange(6000,8000);
  
  
  targetMsg =  'a level ' + Data.waves.target.level + ' ' + Data.waves.target.type + ' at ' + Data.waves.target.x +'/'+ Data.waves.target.y;
  
  
  if (MyAjax.marchBusy > 0){
  
   MyAjax.marchBusy = totalMarches;
   if(MyAjax.marchBusy==0) t.attackBusy = false;
  
   verboseLog('<b>Wave</b> attack to ' + targetMsg + ' delayed due to <b>'+ totalMarches +'</b> pending march request: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('Another march request is pending') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.waveAttackTick, retryDelay);
   return;
  }
  
  if (marching >= Data.options.objAttack.maxMarches){
   verboseLog('<b>Wave<b> attack to ' + targetMsg + ' delayed due to <b>march limit</b> reached: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('March limit reached') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   return;
  }
  
  if (getMusterPointSlots(0) <= 0) {
   verboseLog('<b>Wave</b> attack to ' + targetMsg + ' delayed due to <b>insufficent march slots</b>: retry in ' + timeFormat(retryDelay/1000));
   t.dispFeedback(translate('Muster Point') +' '+ translate('Full') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.waveAttackTick, retryDelay);
   return;
  }
  
  availableGeneral = getAvailableGeneral();
  
  if (availableGeneral == null) {
   verboseLog('<b>Wave</b> attack to ' + targetMsg + ' delayed due to <b>insufficent generals</b>: retry in ' + timeFormat(retryDelay/1000));
   t.dispFeedback(translate('No generals available') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.waveAttackTick, retryDelay);
   return;
  }

  waveUnits = t.checkTroopsWave(0, Data.waves.target.troopsWave);
  if (waveUnits !== null) {
   verboseLog('<b>Wave</b> attack to ' + targetMsg + ' delayed due to <b>' + waveUnits +' units</b>: retry in ' + timeFormat(retryDelay/1000));
   t.dispFeedback(waveUnits + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.waveAttackTick, retryDelay);
   return;
  }
  
  
  var units = Object.clone(Data.waves.target.troopsWave);
  // Check Dragon Wave
  for (var dragonType in Data.waves.target.dragonsWave){
   if ( parseInt(Data.waves.target.dragonsWave[dragonType]) ){
    if ( (t.currentWave % Data.waves.target.dragonsWave[dragonType]) == 0){
     units[dragonType] = 1;
    }
   }
  }

  // All prerequisite checks are done so march request can be sent
  verboseLog('Wave attack to ' + targetMsg + ' Attempted');
  
  
  targetMsg = '#.'+ t.currentWave + ' ' + targetMsg;
  // Add Units to targetMsg
  targetMsg += '<br>' + translate('Sending') + ': ';
  var unitsMsg = [];
  for (var name in units){
   if(units[name] > 0){
    unitsMsg.push(translate(name) + '(' + units[name] + ')');
   }
  }
  targetMsg += unitsMsg.join(' + ');
  
    
  new MyAjax.marchSend (Seed.cities[0].id, Data.waves.target.x, Data.waves.target.y, availableGeneral.id, units, 'waves', function (r) {
   var t = Tabs.Waves, waveDelay, retryDelay;
   if (r.ok && r.dat.result && r.dat.result.success)
   {
    Marches.add(r.dat.result.job.march_id, 'waves');
    
    ++Data.marches.countForLimit;
    ++t.currentWave;
   
    t.attackErrors = 0;
    
    if ( Data.marches.countForLimit > 49 )
    {
     if ( parseInt(serverTime() - Data.marches.startAt) < 3600 ) {
      waveDelay = parseInt( (3600 - (serverTime() - Data.marches.startAt)) * 1000 );
      setTimeout(function(){t.dispFeedback(translate('Attacks stopped momentarily to prevent server blocking') + ' - ' + translate('waiting') + ': ' + timeFormat(waveDelay/1000))}, Data.waves.iterationMin*500 );
     } else {
      Data.marches.startAt = serverTime();
      Data.marches.countForLimit = 1;
     }
    }
    else if ((Data.marches.countForLimit % 15) == 0)
    {
     waveDelay = 45 * totalMarches * 1000;
     setTimeout(function(){t.dispFeedback(translate('Attacks stopped momentarily to prevent server blocking') + '<br>' + translate('waiting') + ': ' + timeFormat(waveDelay/1000))}, Data.waves.iterationMin*500 );
    }
    else {
     waveDelay = Math.randRange(Data.waves.iterationMin*1000, Data.waves.iterationMax*1000);
     setTimeout(function(){t.dispFeedback('')}, parseInt(waveDelay/2) );
    }

    verboseLog('Wave attack to: ' + targetMsg + ' Successfully');
    
    actionLog(translate('Wave attack to') + ': ' + targetMsg);
    
    t.dispFeedback (translate('Wave attack to')+ ': ' + targetMsg);

    //target.lastAttack = serverTime();
    t.attackTimer = setTimeout (t.waveAttackTick, waveDelay);
   }
   else {
    ++t.attackErrors;
    retryDelay = 60000 * (t.attackErrors * t.attackErrors);
    
    verboseLog('<b>Wave<b> attack to: ' + targetMsg + ' <b>failed</b> and returned error' + ': ' + r.errmsg+ ' - retrying in ' + timeFormat(retryDelay/1000));
    
    actionLog(translate('Wave attack to')+ ' ' + targetMsg + ' ' + translate('failed'));

    t.dispFeedback(translate('Wave attack to')+ ' ' + targetMsg + ' failed');
    
    if (r.status == 509){
     retryDelay = 600000;
     verboseLog('<b>Attack</b> to ' + targetMsg + ' failed - <b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(retryDelay/1000));
    
     t.dispFeedback(translate('Attack to') + ' ' + targetMsg + ' ' + translate('failed')+' - '+translate('Rate Limit Exceeded because there were too many requests') + ' - ' + translate('Retry in') +' '+ timeFormat(retryDelay/1000));
    }
    
    t.attackTimer = setTimeout(t.waveAttackTick, retryDelay);

   }
   // Erase feedback message
   setTimeout(function(){t.dispFeedback('');}, parseInt((waveDelay||retryDelay)/2) );
  });
 },

 // returns null if ok, else error message
 checkTroopsWave : function (cityIdx, troops){
  var totalTroops = 0;
  for (var p in troops){
   if (troops[p] > 0){
    totalTroops += troops[p];
    if (Seed.cities[cityIdx].units[p] < troops[p]){
     return (translate('Not enough') + ' ' + translate(p));
    }
   }
  }
  if (totalTroops <= 0){
   return (translate('No Troops Defined'));
  }
  return null;
 },

 marchTick : function (){
  var t = Tabs.Waves;
  clearTimeout (t.marchTimer);
  Marches.updateTable($id(UID['tabWave_Marches']), 'waves');
  t.marchTimer = setTimeout (t.marchTick, 1000);
 },
 
 // Calls Map.getTargetByCoords
 eventCoords : function (event){
  var ex = $id(UID['tabWave_CoordsX']);
  var ey = $id(UID['tabWave_CoordsY']);
  var x = parseIntZero (ex.value);
  var y = parseIntZero (ey.value);
  ex.value = x;
  ey.value = y;
  
  $J('#'+UID['tabWave_Distance']).html( getDistance(Data.map.x, Data.map.y, x, y) );
  
  $J('#'+UID['tabWave_Tile']).html('&nbsp;');
  
  if (x < 0 || x > 749){
   if(x < 0){
    while (x < 0){
     x = 750 + x;
    }
   } else {
    while (x > 749){
     x = x - 750;
    }
   }
   ex.style.backgroundColor = 'red';
   return;
  }
  if (y < 0 || y > 749){
   if(y < 0){
    while (y < 0){
     y = 750 + y;
    }
   } else {
    while (y > 749){
     y = y - 750;
    }
   }
   ey.style.backgroundColor = 'red';
   return;
  }
  
  Data.waves.target.x = x;
  Data.waves.target.y = y;
  
  ey.style.backgroundColor = '';
  ex.style.backgroundColor = '';
  Map.getTargetByCoords(x, y, function(target){
   if (target){
    var type = Map.typeName[target.t] || target.t;
    Data.waves.target.type = type;
    Data.waves.target.level = target.l;
    
    var attColor = target.at ? '#000' : '#C22';
    
    var html = [
     '<font color=' + attColor + '>'
    ,' <b>' + translate(type) + '&nbsp;' + translate('Level') + '&nbsp;' + target.l + '</b>'
    ,'</font>'
    ];
    if (target.pN) {
     html = html.concat([
       '<br>' + translate('City') + ': <b>' + target.n + '</b> - '
     , translate('Alliance') + ': <b>' + (target.aN ? target.aN : '----') + '</b>'
     , '<br>' + translate('Name') + ': <b>' + target.pN + '</b> - '
     , translate('Level') + ': <b>' + target.pL + '</b> - '
     , translate('Might') + ': <b>' + target.pM + '</b>'
     ]);
    }
    
    $id(UID['tabWave_Tile']).innerHTML = html.join('');
   }
  });
 },


 show : function () {
  var t = Tabs.Waves;
  t.marchTick();
 },
 hide : function (){
  var t = Tabs.Waves;
  clearTimeout (t.marchTimer);
 }

}; // END Tabs.Waves



//******************************** Attacks Tab *****************************
// References to camp and camps changed to mapObject to make sure that other data does not overwrite the camps
Tabs.Attacks = {
 tabOrder  : ATTACK_TAB_ORDER,
 tabLabel  : 'Attacks',
 tabDisabled  : !ATTACK_TAB_ENABLE,
 lastSubTab  : 'tabAttackLevels',
 container  : null,
 attackTimer  : null,
 marchTimer  : null,
 lastAttack  : 0,
 attackErrors : 0,
 checkMapBusy : false,
 maxRadius  : 35, // WARNING: Changing this value can produce Kabam account banned for abuse of requests to the server
 curRunStart  : 0,
 contentType  : 0, // 0 = levels, 1 = config, 2 = targets, 3 = stats, 4 = mapTypes these should be enums but Javascript doesn't support that type
 selectedMapName : kAnthropusCamp,

 init : function (div){
  var t = Tabs.Attacks;
  t.container = div;
  
  // This is where we store the troops type and quantity from the Levels sub-tab
  // TBD: To save different configurations for wildernesses, ant camps, and cities/outposts
  // I will use a multidimensional array. The first index is the row, the second is the column
  // For our purposes the row is the map type selector, and the column is the troop type and quantity data {}
  //
  // [wilderness][0(null)][1][2][3][4][5][6][7][8][9][10]
  // [antcamps][0(null)][1][2][3][4][5][6][7][8][9][10]
  // [city][0(null)][1][2][3][4][5][6][7][8][9][10]
  //
  for (var x=1; x < 12; x++){
   if (!Data.options.objAttack.troops[x]){
    Data.options.objAttack.troops[x] = {};
   }
  }
  
  var html = [
   '<div id=' + setUID('tabAttack_Title') + ' class=' + UID['title'] + '>' + translate('Attack') + ' ' + translate(Data.map.selected) + ' </div>'
  ,'<div class=' + UID['status_ticker'] + ' id='+ setUID('tabAttack_Status') + ' style="margin-bottom:5px !important">'
  ,' <center>'
  ,'  <input type=button value="OnOff" id=' + setUID('tabAttack_OnOff') + ' />'
  ,' </center>'
  ,' <div class=' + UID['status_report'] + ' style="margin-top:5px;height:140px; max-height:140px; overflow-y:auto;">'
  ,'  <table id=' + setUID('tabAttack_Marches') + ' class=' + UID['table'] + '>'
  ,'  </table>'
  ,' </div>'
  ,' <div id=' + setUID('tabAttack_Feedback') + ' class=' + UID['status_feedback'] + '></div>'
  ,'</div>'
  ,'<ul class=tabs>'
  ,' <li class="tab first"><a id=' + setUID('tabAttackLevels') + '>' + translate('Levels')     + '</a></li>'
  ,' <li class=tab><a id='         + setUID('tabAttackTarget') + '>' + translate('Targets')    + '</a></li>'
  ,' <li class=tab><a id='         + setUID('tabAttackStats')  + '>' + translate('Statistics') + '</a></li>'
  ,' <li class=tab><a id='         + setUID('tabAttackMaps')   + '>' + translate('Map')        + '</a></li>'
  ,' <li class=tab><a id='         + setUID('tabAttackConfig') + '>' + translate('Options')    + '</a></li>'
  ,'</ul>'
  ,'<div id=' + setUID('tabAttack_Content') + ' style="padding-top:5px; height:445px; background-color:white"></div>'
  ];
  
  $J(t.container).html( html.join('') );
  

  // Add the event listeners
  $J('#'+UID['tabAttack_OnOff']).click (function (){
   t.setAttackEnable (!Data.options.objAttack.enabled);
  });
  $J('#'+UID['tabAttackLevels']).click (t.tabAttackLevels);
  $J('#'+UID['tabAttackTarget']).click (t.tabAttackTarget);
  $J('#'+UID['tabAttackStats']).click (t.tabAttackStats);
  $J('#'+UID['tabAttackMaps']).click (t.tabAttackMaps);
  $J('#'+UID['tabAttackConfig']).click (t.tabAttackConfig);
  
  if (Data.options.objStats == null){
   t.clearStats();
  }
  
  if (Data.options.objAttack.maxMarches == undefined){
   Data.options.objAttack.maxMarches = 10;
  }
  
  Messages.addBattleReportListener(t.gotBattleReport);
  setTimeout (Marches.check, 60000); 
  t.tabAttackLevels();
  
  window.addEventListener ('unload', t.onUnload, false);
  t.setAttackEnable (Data.options.objAttack.enabled);
  
  /*
  for (var p in Data.options.objMarches)
  {
   if (Seed.marches[Data.options.objMarches[p].id])
   {
    Seed.marches[Data.options.objMarches[p].id].ownerId = 'camp';
   }
  }   
  */
 },

 firstShow : true,
 show : function () {
  var t = Tabs.Attacks;
  t.marchTick();
  if (t.firstShow){
   t.marchTick();
   t.contentType = Data.options.attackTab;
   setTimeout (function (){
    // Do not automatically scan the map, wait for the user to initiate the scan on the maps sub-tab
    //t.checkMapData();
    t.firstShow = false;
   }, 0);
  }
  if (t.contentType == 2){
   $id(UID['tabAttack_Content']).scrollTop = 0;
  }

  switch (t.contentType) {
   case 0: t.tabAttackLevels(); break;
   case 1: t.tabAttackConfig(); break;
   case 2: t.tabAttackTarget(); break;
   case 3: t.tabAttackStats(); break;
   case 4: t.tabAttackMaps(); break;
  }
 },
 hide : function (){
  var t = Tabs.Attacks;
  clearTimeout (t.marchTimer);
 },

 onUnload : function () {
  logit('Tabs.Attacks.onUnload');
  var t = Tabs.Attacks;
  if (Data.options.objAttack.enabled){
   Data.options.objStats.runTime += (serverTime()-t.curRunStart);
  }
  Data.options.attackTab = t.contentType;
 },
 
 checkMapData : function () {
  var t = Tabs.Attacks;
  
  if (t.checkMapBusy){
   return false;
  }
  else if (Data.map.terrains[Data.map.selected].length==0) {
  
   t.checkMapBusy = true;
   
   t.setAttackEnable (false);
   
   var dialogbox = dialogBox({
    id   : setUID('dialog-scanmap'),
    centerTo : t.container,
    overlay  : true,
    title  : 'Scanning Map',
    html  : translate('Scanning Map').replace('$NUM$','15')+ '<br><br><div class=progressbar></div>'
   });
   
   var x = Data.map.x;
   var y = Data.map.y;
   var radius = Data.map.radius = 15;
   
   Map.scanMap (x,y, radius, function(res){
    if (res == null){
     dialogbox.html('<B>' + translate('Bummer, there was an error while scanning the map') + '.</B>');
     Tabs.Attacks.checkMapBusy = false;
     return;
    }
    if(res.done){
     verboseLog('scanMap: complete!');

     Tabs.Attacks.checkMapBusy = false;
     
     var html = '<center><br>' + translate('complete') + '!<br><br><table>';
     for (var type in res.terrains){
      html += '<tr><td>'+ translate(type) + '&nbsp;</td><td>&nbsp;' + res.terrains[type].length + '</td></tr>';
     }
     html += '</table></center>'
     
     dialogbox.html(html);
     dialogbox.centerTo();
     dialogbox.buttons([
      {
       text: translate("Ok"),
       click: function() { 
        dialogbox.destroy();
       }
      }
     ]);
     
     // Refresh the Tab Content
     t.tabAttackTarget();
     
    } else if(res.init){
     StepTimeBar.start({target:$J('#'+UID['dialog-scanmap']+' .progressbar'), steps:Map.steps});
    }    
    else {
     StepTimeBar.update(Map.step);
    }
   });
   return false;
  }
  return true;
 },

 gotBattleReport : function (rpt){
  var t = Tabs.Attacks;
  //logit ('Tabs.Attacks.gotBattleReport'); 
  // tie report to march id ...
  var march_id = null;
  for (var id in Data.marches.attacks )
  {
   var march = Data.marches.attacks[id];
   
   if (march.x == rpt.report.location.x && 
    march.y == rpt.report.location.y &&
    march.general.id == rpt.report.attacker.general.id
    ){  // TODO: time and troops check here
     march_id = id;
     break;
   }
  }
  if (march_id)
   t.trackStats (march_id, rpt);
  
  if (!Data.options.objAttack.deleteObjAttacks && !Data.options.objAttack.stopAttackOnLoss ){
   return;
  }
  //logit (inspect (rpt, 8, 1));
  if (Data.options.objAttack.stopAttackOnLoss)
  {
   for (var p in rpt.report.attacker.units)
   {
    if (rpt.report.attacker.units[p][0] != rpt.report.attacker.units[p][1])
    {
     var ts = new Date(rpt.report_notification.created_at * 1000).myString();
     t.abort (translate('Troops lost') +'! ('+ ts +')');
     return;
    }
   }
  }
  if (Data.options.objAttack.deleteObjAttacks && rpt.report.attacker.name == Seed.player.name){
   Messages.deleteMessage (rpt.report_notification.id);
  }
 },

 setAttackEnable : function (onOff){
  var t = Tabs.Attacks;
  clearTimeout (t.attackTimer);
  var but = $id(UID['tabAttack_OnOff']);
  Data.options.objAttack.enabled = onOff;
  if (onOff){
   but.value = translate('Attacking').toUpperCase();
   but.className = UID['btn_on'];
   t.curRunStart = serverTime();
   t.currentWave = 1;
   if ( !Data.waves.enabled ){
    Data.marches.countForLimit = 1;
    Data.marches.startAt = serverTime();
   }
   
   t.autoCheckTargets();
  } 
  else {
   if (t.curRunStart != 0)
   Data.options.objStats.runTime += (serverTime()-t.curRunStart);
   but.value = translate('Disabled').toUpperCase();
   but.className = UID['btn_off'];
   t.dispFeedback ('');
  }
 },

 abort : function (msg){
  var t = Tabs.Attacks;
  t.setAttackEnable (false);
  t.dispFeedback (msg);
  actionLog (msg);
 },

 marchTick : function (){
  var t = Tabs.Attacks;
  clearTimeout (t.marchTimer);
  Marches.updateTable($id(UID['tabAttack_Marches']), 'attacks');
  t.marchTimer = setTimeout (t.marchTick, 1000);
 },

 dispFeedback : function (msg){
  if (msg && msg!=''){
   msg = new Date().toTimeString().substring (0,8) +'&nbsp;'+ msg;
  }
  $id(UID['tabAttack_Feedback']).innerHTML = msg;
 },

 autoCheckTargets : function (){
  var t = Tabs.Attacks;
  var now = serverTime();
  var cityIdx = 0;
  var targetMsg='', retryDelay, availableGeneral, marching = 0, totalMarches=0, id;
  
  clearTimeout (t.attackTimer);
  
  // Don't do anything if attacks are not enabled
  if (!Data.options.objAttack.enabled){
   return;
  }
  
  
  var minTime = 700000;
  var maxTime = 0;
  for (id in Seed.marches){
   ++totalMarches;
   if (Seed.marches[id].status == 'marching'){
    ++marching;
   }
   var restTime = ( Seed.marches[id].run_at - parseInt(serverTime()) ) * (Seed.marches[id].status=='marching'?2:1);
   minTime = minTime < restTime ? minTime : restTime;
   maxTime = maxTime > restTime ? maxTime : restTime;
  }
  
  retryDelay = totalMarches ? minTime*1000 + Math.randRange(2000,5000) : Math.randRange(6000,8000);
  
  
  // back off for 1 second and retry if MyAjax.march busy (general,troops,etc may about to be used)
  if (MyAjax.marchBusy > 0){
  
   MyAjax.marchBusy = totalMarches;
   if(MyAjax.marchBusy==0) t.attackBusy = false;
  
   verboseLog('<b>Attack</b> to ' + targetMsg + ' delayed due to <b>'+ totalMarches +' pending</b> march request: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('Another march request is pending') + ': ' +translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   return;
  }
  
  // Find the map data
  if ( !t.checkMapData() ){
   return;
  }

  
  if (marching >= Data.options.objAttack.maxMarches){
   verboseLog('<b>Attack</b> to ' + targetMsg + ' delayed due to <b>march limit</b> reached: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('March limit reached') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   return;
  }
  
  if (getMusterPointSlots (0) <= 0){
   verboseLog('<b>Attack</b> to ' + targetMsg + ' delayed due to </b>insufficent march</b> slots: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback (translate('Muster Point') +' '+ translate('Full') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   return;
  }
  
  availableGeneral = getAvailableGeneral();

  if (availableGeneral == null) {
   verboseLog('<b>Attack</b> to ' + targetMsg + ' delayed due to <b>insufficent generals</b>: retry in ' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('No Generals Available') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   return;
  }

  // Get the next target, make sure we have sufficient troops
  var nextTarget = t.getNextAttackTarget();
  if(nextTarget){
   var checkTroops = checkAvailableTroops (0, nextTarget.l);
   if ( checkTroops.total > 0 ) {
    targetMsg = nextTarget.x + '/' + nextTarget.y;
    /*
    verboseLog('Opening the map on the last position: ' + Map._lastPos.x + '/' + Map._lastPos.y);
    t.dispFeedback(translate('Preparing Attack') +': '+ translate('Opening the map on the last position')+'...');
    Map.simulateOpenMap(null, null, function(target){
        verboseLog('Map: go to position: ' + nextTarget.x + '/' + nextTarget.y);
     t.dispFeedback(translate('Preparing Attack') +': '+ translate('Going to the coords') + ' ' + nextTarget.x + '/' + nextTarget.y +' ...');
     setTimeout(function(){
      Map.simulateOpenMap(nextTarget.x, nextTarget.y, function(target){
       setTimeout(function(){
    */
        t.sendAttack (0, nextTarget, availableGeneral, function (r){
         var t = Tabs.Attacks, attackDelay, retryDelay;
         if (r.ok){
          var delayMin = Data.options.objAttack.delayMin;
          var delayMax = Data.options.objAttack.delayMax;
          if (delayMin < ATTACK_MIN_DELAY){
           if(delayMax > delayMin + 30){
            delayMin = delayMax - 30;
           } else {
            delayMin = ATTACK_MIN_DELAY;
            delayMax = delayMin + 30;
           }
           Data.options.objAttack.delayMin = delayMin;
           Data.options.objAttack.delayMax = delayMax;
          }
          
          if ( Data.marches.countForLimit > 49 )
          {
           if ( parseInt(serverTime() - Data.marches.startAt) < 3600 ) {
            attackDelay = parseInt( (3600 - (serverTime() - Data.marches.startAt)) * 1000);
            setTimeout(function(){t.dispFeedback(translate('Attacks stopped momentarily to prevent server blocking') + ' - ' + translate('waiting') + ': ' + timeFormat(attackDelay/1000))}, delayMin*500 );
           } else {
            Data.marches.startAt = serverTime();
            Data.marches.countForLimit = 1;
           }
          }
          else if ((Data.marches.countForLimit % 15) == 0)
          {
           attackDelay = 45 * totalMarches * 1000;
           setTimeout(function(){t.dispFeedback(translate('Attacks stopped momentarily to prevent server blocking') + '<br>' + translate('waiting') + ': ' + timeFormat(attackDelay/1000))}, delayMin*500 );
          }
          else {
           attackDelay = Math.randRange(delayMin*1000, delayMax*1000);
           setTimeout(function(){t.dispFeedback('')}, parseInt(attackDelay/2) );
          }
          
          t.attackTimer = setTimeout(t.autoCheckTargets, attackDelay);
          
         } else {
          retryDelay = 30000 * (t.attackErrors * t.attackErrors);
          
          if (r.status == 509){
           retryDelay = 600000;
           verboseLog('<b>Attack</b> to ' + targetMsg + ' failed - <b>Rate Limit Exceeded</b>, too many requests! -  Retry in :' + timeFormat(retryDelay/1000));
          
           t.dispFeedback(translate('Attack to') + ' ' + targetMsg + ' ' + translate('failed')+' - '+translate('Rate Limit Exceeded because there were too many requests') + ' - ' + translate('Retry in') +' '+ timeFormat(retryDelay/1000));
          }
          
          t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
         }
        }); // End sendAttack
    /*
       }, Math.randRange(7000,10000));
      }); // End Map.getTargetCoords Target
     }, Math.randRange(6000,9000));
    }); // End Map.getTargetCoords Last Target
    */
    return;                
   } else {
    verboseLog('<b>Attack</b> to ' + targetMsg + ' delayed due to <b>insufficient troops</b>: retry in ' + timeFormat(retryDelay/1000));

    t.dispFeedback(translate('Not enough') + ' ' + translate('Troops') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
    t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
   }
  } else {
   verboseLog('<b>Attack</b> to ' + targetMsg + '<b>Requirements Unmet</b>: Retry in' + timeFormat(retryDelay/1000));

   t.dispFeedback(translate('Requirements Unmet') + ': ' + translate('Retry in') + ' ' + timeFormat(retryDelay/1000));
   t.attackTimer = setTimeout(t.autoCheckTargets, retryDelay);
  }
 },

 // notifies with true for success, false if error
 sendAttack : function (cityIdx, target, general, notify){
  var t = Tabs.Attacks;
  var now = serverTime();
  if (t.attackBusy){
   t.dispFeedback (translate('Error')+ ': ' +translate('sendAttack is busy, no response from server?'));
   return;
  }
  
  var targMsg =  translate('Attack sent to') + ': ' + translate(Data.map.selected) + ' ' + translate('Level') + ' ' + target.l + ' ' + translate('at') + ' ' + target.x + '/'+ target.y;
  
  var targMsgLog =  'Attack sent to: ' + Data.map.selected + ' Level ' + target.l + ' at ' + target.x + '/'+ target.y;
  
  
  verboseLog(targMsgLog +' Attempted');
  
  t.attackBusy = true;
  t.lastAttack = now;

  new MyAjax.marchSend (Seed.cities[cityIdx].id, target.x, target.y, general.id, Data.options.objAttack.troops[target.l], 'attacks', function (r) {
   t.attackBusy = false;
   if (r.ok && r.dat.result && r.dat.result.success) {
    
    Marches.add(r.dat.result.job.march_id, 'attacks');
    
    ++Data.marches.countForLimit;
    
    target.lst = now;
    
    t.attackErrors = 0;
    
    verboseLog(targMsgLog +' Successfully');
    
    t.dispFeedback(targMsg);
    
    if (Data.options.objAttack.logAttacks){
     actionLog(targMsg);
    }

    if (notify){
     notify(r);
    }
   }
   else {
    ++t.attackErrors;

    verboseLog(targMsgLog + ' <b>failed and returned error</b>: ' + r.errmsg);
    
    actionLog(targMsg + ' ' + translate('failed'));
    
    t.dispFeedback(targMsg + ' failed');
    
    if (notify){
     notify(r);
    }
   }
  });
 },

 // return the nextTarget that is next to be attacked, if we are at the last object in the last, return the first object
 getNextAttackTarget : function (type){
  var t = Tabs.Attacks;
  var lastAttack = 0;
  var nextTarget = null;
  var target = null;
  var objAttack = Data.options.objAttack;
  
  type = type || Data.map.selected;
  
  if(Data.map.terrains[type] == undefined) {
   return null;
  }
  
  // Look through all the targets
  for (var i=0; i < Data.map.terrains[type].length; i++){
   target = (Data.map.terrains[type])[i];
   // Is this target attackable?
   if (target.at) {
    // Does it fit within the config specifications (distance and level)?
    if ( objAttack.levelEnable[target.l] && 
      (objAttack.levelDist[target.l] == 0 || target.d <= objAttack.levelDist[target.l]) &&
      (checkAvailableTroops(0, target.l)).total > 0 
     ) {
      // Has the target never been attacked?
      if (target.lst == 0) {
       nextTarget = target;
       break;
      } 
      else if (lastAttack == 0) {
       // Yes, this target is next (so far)
       lastAttack = target.lst;
       nextTarget = target;
      }
      else if (lastAttack > target.lst) { // Was the previous target attacked before this target?
       // Yes, this target is next (so far)
       lastAttack = target.lst;
       nextTarget = target;
       break;
      }
    }
   }
  }
  
  // No target reaches the specified requirements
  if(nextTarget == null){
   return;
  }
  
  // This is complicated by the fact that the last attacked target in the list may not be the last physical entry, just the one that fits
  // the config info (distance, level enables, attackable)
  // Find the last matching target in the list
  
  var objs = Data.map.terrains[type];
  var lastMatchingTarget = null;
  for (var j=objs.length-1; j>0; j--) {
   target = objs[j];
   if (target.at) {
    if ( objAttack.levelEnable[target.l] &&
     (objAttack.levelDist[target.l] == 0 || target.d <= objAttack.levelDist[target.l])
     ){
      lastMatchingTarget = target;
      break;
    }
   }  
  }
  
  // Is the next target the last matching target?
  if (nextTarget == lastMatchingTarget) {
   for (var k=0; j < objs.length; k++) {
    target = objs[k];
    if (target.at) {
     if ( objAttack.levelEnable[target.l] &&
     (objAttack.levelDist[target.l] == 0 || target.d <= objAttack.levelDist[target.l])
     ){
      // Make the next target the first matching target in the list
      nextTarget = target;
      break;
     }
    }
   }
  }

  // Return the next target
  return nextTarget;
 },

 // return array of targets that satisfy config (max distance, level enables)
 getActiveObjectList : function (type){
  var t = Tabs.Attacks;
  var ret = [];
  
  type = (type != undefined ? type : Data.map.selected);
  
  if(Data.map.terrains[type]){
   for (var i=0; i < Data.map.terrains[type].length; i++){
    var target = (Data.map.terrains[type])[i];
    if ( Data.options.objAttack.levelEnable[target.l] &&
     (Data.options.objAttack.levelDist[target.l] == 0 || target.d <= Data.options.objAttack.levelDist[target.l])
      ){
      ret.push (target);
    }
   }
  }
  return ret;
 },

 checkAttack : function (target, notify){
  var t = Tabs.Attacks;
  var cityId = Seed.cities[0].id;
  var cityIdx = 0;
  var availableGeneral;
  
  // check troops
  var troops = Data.options.objAttack.troops[target.l];
  var totalTroops = 0;
  for (var unit_type in troops){
   if (troops[unit_type] > 0){
    totalTroops += troops[unit_type];
    if (Seed.cities[cityIdx].units[unit_type] < troops[unit_type]){
     notify (translate('Not enough') +' '+ translate(unit_type));
     return;
    }
   }
  }
  
  if (totalTroops <= 0){
   notify (translate('No Troops Defined'));
   return;
  }
  
  
  var musterPointLimit = getMusterPointLevel (cityIdx) * 10000;
  if (totalTroops > musterPointLimit) {
   notify (translate('Too many troops for muster point level'));
   return;
  }

  if (getMusterPointSlots (cityIdx) <= 0){
   notify (translate('Muster Point') +' '+ translate('Full'));
   return;
  }

  if ((availableGeneral = getAvailableGeneral ()) == null){
   notify (translate('No Generals Available'));
   return;
  }
  
  
  var targMsg =  translate('Manual attack sent to') + ': ' + translate(Data.map.selected) + ' ' + translate('Level') + ' ' + target.l + ' ' +  translate('at') + ' ' + target.x +'/'+ target.y;
  
  var targMsgLog =  'Manual attack sent to: ' + Data.map.selected + ' Level ' + target.l + ' at ' + target.x +'/'+ target.y;
  
  verboseLog(targMsgLog +' Attempted');
  
  new MyAjax.marchSend (cityId, target.x, target.y, availableGeneral.id, troops, 'attacks', function (r) {
   if (r.ok) {
   
    Marches.add(r.dat.result.job.march_id, 'attacks');
    
    ++Data.marches.countForLimit;
    
    target.lst = serverTime();
    
    
    verboseLog(targMsgLog +' Successfully');
    t.dispFeedback(targMsg);
    
    if (Data.options.objAttack.logAttacks){
     actionLog(targMsg);
    }
    
    notify(null);
   }
   else {
    verboseLog(targMsgLog +' <b>failed and returned</b> error: '+ r.errmsg);

    t.dispFeedback (translate('Error') + ': ' + r.errmsg);
    notify(null);
    //notify(kAttackErr + r.errmsg);
   }
  });
 },
 

 
 //*** Attacks Tab - Levels Sub-Tab ***
 //----------------------------------------------------------------------------
 units_type : [kPorter, kConscript, kSpy, kHalberdsman, kMinotaur, kLongbowman, kSwiftStrikeDragon, kBattleDragon, kArmoredTransport, kPackDragon, kGiant, kFireMirror, kAquaTroop, kStoneTroop, kFireTroop, kWindTroop],
 dragonTypes : [kGreatDragon,kWaterDragon,kStoneDragon,kFireDragon,kWindDragon],
 
 tabAttackLevels : function (){
  var t = Tabs.Attacks;
  
  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabAttackLevels']).addClass('selected');
  $J('#'+UID['tabAttackLevels']).css('z-index', '1');
  t.lastSubTab = 'tabAttackLevels';
  
  t.contentType = 0;
  
  var city = Seed.cities[0];

  // New content area here
  var html = [
   '<div class="' + UID['title'] + '">' + translate('Attacks') + '&nbsp;' + translate(Data.map.selected) + '</div>'
  ,'<div style="overflow-y:auto">'
  ,' <table class="' + UID['compact_table'] + ' ' + UID['hide_inputbox'] +'">'
  ,'  <tr class=' + UID['row_top_headers'] + '>'
  ,'   <td style="background:none !important;"></td>'
  ,'   <td align=center colspan=11>&nbsp;' + translate('Levels') + '&nbsp;</td>'
  ,'  </tr>'
  ,'  <tr align=center class=' + UID['row_headers'] + '>'
  ,'   <td style="background:none !important;"></td>'
  ,'   <td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td>'
  ,'  </tr>'
  ,'  <tr align=center>'
  ,'   <td class=left>' + translate('Enable') + ': </td>'
  ];
  
  for (var x=1; x < 12; x++){
   html = html.concat([
    '  <td>'
   ,'  <label>'
   ,'  <input type=checkbox id=' + setUID('tabAttackLevels_LvlOnOff_' + x) + ' ref=' + x + ' ' + (Data.options.objAttack.levelEnable[x] ? ' checked' : '') + ' />'
   ,'  </label>'
   ,'  </td>'
   ]);
  }
  
  html = html.concat([
   '  </tr>'
  ,'  <tr align=center>'
  +'   <td class=left>' + translate('Max') + ' ' + translate('Distance').truncate(4,'') + ': </td>'
  ]);
  
  for (var x=1; x < 12; x++){
   html = html.concat([
    '  <td>'
   ,'  <input type=text id=' + setUID('tabAttackLevels_LvlDist_' + x) + ' ref=' + x + ' maxlength=2 style="width:36px" value="' + Data.options.objAttack.levelDist[x] + '" />'
   ,'  </td>'
   ]);
  }
  
  html = html.concat([
  '  </tr><tr>'
  ,'    <td><div class=short></div></td>'
  ,'   </tr>'
  ]);
  
  var currentTroops = [];
  for (i=0; i < t.units_type.length; i++)
  {
   if(getTroopNumbers(city, t.units_type[i]).total)
   {
    var color = '#FFF';
    if(i<5) color = '#DDD';
    else if(i<8) color = '#BEB';
    else if(i<10) color = '#EEB';
    else if(i<12) color = '#BBE';
    else if(i<16) color = '#BBE';
    else color = '#EBB';
    
    html = html.concat([
    ' <tr style="background-color:' + color + ';">'
    ,'  <td class=left>'
    ,'   <span title="' + translate(t.units_type[i]) + '">' + translate('~' + t.units_type[i]) + ':<span>'
    ,'  </td>'
    ]);
    
    for (var x=1; x < 12; x++)
    {
     var num = Data.options.objAttack.troops[x][t.units_type[i]];
          
     if (!num){ num = 0; }
     
     html = html.concat([     
      '<td>'
     ,'<input type=text id=' + setUID('tabAttackLevels_LvlTroops_' + x + '_' + i) + ' ref=' + (x + '_' + i) + ' maxlength=6 size=2 style="width:34px;' + (num ? '' : 'color:#888;') + '" value="' + num + '" title="" />'
     ,'</td>'
     ]);
    }
    html = html.concat(['</tr>']);
    currentTroops.push(i);
   }
  }
  html = html.concat(['</table></div>']);
  
  $J('#'+UID['tabAttack_Content']).html( html.join('') );

  // add event listeners ...
  for (var x=1; x < 12; x++){
   $J('#'+UID['tabAttackLevels_LvlOnOff_'+ x]).change(enableChanged);
  }
  for (var x=1; x < 12; x++){
   $J('#'+UID['tabAttackLevels_LvlDist_'+ x]).change(distChanged);
  }
  
  for (i=0; i < currentTroops.length; i++){
   for (var x=1; x < 12; x++){
    $J('#'+UID['tabAttackLevels_LvlTroops_'+ x +'_'+ currentTroops[i]]).change(troopsChanged);
   }
  }
  
  function enableChanged (event){
   var n = parseInt(event.target.getAttribute('ref'));
   Data.options.objAttack.levelEnable[n] = event.target.checked;
  }
  
  function distChanged (event){
   var n = parseInt(event.target.getAttribute('ref'));
   var x = parseIntZero(event.target.value);
   if (isNaN(x) || x < 1 || x > t.maxRadius){
    event.target.style.backgroundColor = 'red';
    dialogError (translate('Distance must be between') + ' 1 ' + translate('and') +' '+ t.maxRadius, t.container);
   } 
   else {
    event.target.value = x;
    event.target.style.backgroundColor = '';
    Data.options.objAttack.levelDist[n] = x;
   }
  }
  
  function troopsChanged (event){
   var args = event.target.getAttribute('ref').split('_');
   var x = parseIntZero(event.target.value);
   if (isNaN(x) || x<0 || x>120000){
    event.target.style.backgroundColor = 'red';
    dialogError (translate('Invalid number of troops',t.container));
   }
   else {
    event.target.value = x;
    Data.options.objAttack.troops[args[0]][t.units_type[args[1]]] = x;
    event.target.style.backgroundColor = '';
    if(parseInt(event.target.value)>0){
     event.target.style.color = '#000';
    }
   }
  }
 },


 //*** Attacks Tab - Targets Sub-Tab ***
 //----------------------------------------------------------------------------
 tabAttackTarget : function (){
  var t = Tabs.Attacks;

  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabAttackTarget']).addClass('selected');
  $J('#'+UID['tabAttackTarget']).css('z-index', '1');
  t.lastSubTab = 'tabAttackTarget';
  
  t.contentType = 2;

  var timer = null;
  
  t.checkMapData();
  
  // Owned resources have a red background color and white text
  var targets = t.getActiveObjectList();
  if (targets.length == 0){
   t.dispFeedback ( translate('Use the Levels Tab to select attack areas') );
  }

  // Sort targets list by distance
  targets.sort(function(a,b){return a.d - b.d;});
  
  $J('#'+UID['tabAttack_Title']).html( translate('Attack') + ' ' + translate(Data.map.selected) );
  $J('#'+UID['tabAttack_Content']).html( '<div id=' + setUID('tabAttackTarget_Content') + '></div>' );
 
  var html = [
   '<div class=' + UID['title'] + '>'
  ,  translate('Attacks') + '&nbsp;' + translate(Data.map.selected)
  ,'</div>'
  ];
  
  // MapChoice Selector
  setUID('tabAttackTarget_MapChoice');
  html = html.concat([
   '<table>'
  ,'<tr>'
  ,' <td align=right>&nbsp;'
  ,'  <b>' + translate('Select') + '<br>' + translate('Targets') + '</b>'
  ,' </td>'
  ,' <td>&nbsp;:'
  ,'  <select id=' + UID['tabAttackTarget_MapChoice'] + '>'
  ]);
  
  for (var type in Data.map.terrains){
   if ( type == kCity ) {
    html = html.concat([
    '<option disabled="disabled">-----------------------</option>'
    ]);
   }
   html = html.concat([
    '<option value="' + type + '" ' + (type==Data.map.selected ? 'selected' : '') + '>'
   , translate(type) 
   ,'</option>'
   ]);
  }
  
  var levelDist = [];
  for (var i=1; i<Data.options.objAttack.levelDist.length; i++){
   if(Data.options.objAttack.levelEnable[i]){
    levelDist.push(Data.options.objAttack.levelDist[i]);
   }
  }
  levelDist.sort(function(a,b){return a-b;});
  var rangeDist = (levelDist.first() != levelDist.last()) ? levelDist.first()+' - '+levelDist.last() : levelDist[0];
  
  html = html.concat([
  ,'  </select>&nbsp;'
  ,' </td>'
  ,' <td>'
  ,'  <span class=jewel>' + targets.length + ' ' + translate('of') + ' ' + Data.map.terrains[Data.map.selected].length + ' (' + translate('Distance') + ' ' + translate('Max') + ': ' +rangeDist + ')</span>'
  ,' </td>'
  ,' </tr>'
  ,'</table>'
  ,'<br>'
  ]);
  
  html = html.concat([
   '<div class="' + UID['scrollable'] + '" style="height:365px;">'
  ,' <table id='+ setUID('tabAttackTarget_Tab') +' class=' + UID['table'] + '>'
  ,'  <tr class='+ UID['row_headers'] +'>'
  ,'   <td valign=middle><b>' + translate('Distance').substring(0,4) + '</b></td>'
  ,'   <td valign=middle><b>' + translate('Coords') + '</b></td>'
  ,'   <td valign=middle><b>' + translate('Level')  + ' </b></td>'
  ,'   <td valign=middle width=65><b>'+ translate('Last Attack').split(' ').join('<br/>') +'</b></td>'
  ,'  </tr>'
  ]);
  
  // Hilite owned wildernesses
  //var ownedWilderness = Seed.player.player_wildernesses;
  
  var bFound = false;
  for (var i=0; i < targets.length; i++){
   
   var refs = Data.map.byCoords[targets[i].x +','+ targets[i].y];
   var targetInfo = ( Data.map.terrains[Map.typeName[refs.t]] )[refs.n];
   
   html = html.concat([
   '<tr id=' + setUID('tabAttackTarget_TabRow_' + i)
   ]);
   if ( targetInfo.pN )
   {
    html = html.concat([
    '  title="'
    , (targetInfo.n || ' ') + '  ('+ translate(Map.typeName[targetInfo.t]) + ') \n'
    , targetInfo.pN + ' (lvl ' + targetInfo.pL + ') - Pwr: ' + targetInfo.pM +' \n'
    , translate('Alliance') + ': ' + (targetInfo.aN || '---')
    ,'"'
    ]);
   }
   
   html = html.concat([
    '>'
   ,'<td>' + targets[i].d +'</td>'
   ,'<td align=center>' + targets[i].x + '/' + targets[i].y + '</td>'
   ,'<td align=center>' + targets[i].l + '</td>'
   ,'<td><span id=' + setUID('tabAttackTarget_lastAttack_'+i) + '> --- </span></td>'
   ,'<td><input id=' + setUID('tabAttackTarget_AttackNow_'+i) + ' ref=' + i + ' class=thin type=button value=" ' + translate('Attack') + '! " />'
   ]);
   
   // Add the skip attack button for cities and outposts
   if ( targetInfo.pN )
   {
    html = html.concat([
     '&nbsp;<input id='+ setUID('tabAttackTarget_SkipAttack_'+i) +' ref='+ i +' type=checkbox '+ (targetInfo.at?'checked':'') +' /></td>'
    , '<td><b>' + targetInfo.pN.truncate(13).replace('...','<span class=jewel>...</span>') + '</b></td>'
    , '<td>' + (targetInfo.aN || '---').truncate(16).replace('...','<span class=jewel>...</span>')
    ]);
   }
   
   html = html.concat(['</td></tr>']);

  }
  
  html = html.concat(['</table></div>']);

  var $container = $J('#'+UID['tabAttackTarget_Content']);
  
  $container.html( html.join('') );
  $container.css('height', $container.parent().innerHeight()-10 + 'px');
  
 
  // Add the event listeners
  $J('#'+UID['tabAttackTarget_MapChoice']).change(onMapChoice);
  
  for (var i=0; i < targets.length; i++) {
   var butAttack = $id(UID['tabAttackTarget_AttackNow_'+ i]);
   $J(butAttack).click (butAttackNow);
   if ( $J('#'+UID['tabAttackTarget_SkipAttack_'+ i]) ){
    $J('#'+UID['tabAttackTarget_SkipAttack_'+ i]).click (toggleAttackable);
   }
   setButtonStyle (butAttack, targets[i].at);    
  }
  
  tick();
  
  function setButtonStyle (element, enabled) {
   if (enabled) {
    element.disabled = false;
    $J(element).removeClass(UID['bnt_red']);
    $J(element).addClass(UID['bnt_green']);
   }
   else {
    element.disabled = true;
    $J(element).removeClass(UID['bnt_green']);
    $J(element).addClass(UID['bnt_red']);
   }
  }
  
 
  function onMapChoice (event){
   var t = Tabs.Attacks;
   
   if (Data.options.objAttack.enabled) {
    // It would be very bad to leave attack on when switching targets. Imagine sending the troops for a wilderness to a city or an ant camp...
    clearTimeout (timer);
    t.setAttackEnable(false);
    t.dispFeedback (translate('Safe Mode') +': '+ translate('Attacks') +' '+ translate('Disabled'));
   }
   
   var element = event.target;
   
   Data.map.selected = Tabs.Attacks.selectedMapName = element.options[element.selectedIndex].value;
   //Data.map.targets = Data.map.terrains[Data.map.selected].clone();
   t.tabAttackTarget();
  }

  function butAttackNow (event){
   var n = parseInt(event.target.getAttribute('ref'));
   var dialogbox = dialogBox({
    id  : setUID('dialog-attacking'),
    centerTo: t.container,
    title : translate('Attacking')
   });
   
   t.checkAttack (targets[n], notify);
   function notify (r){
    if (r!=null){
     dialogbox.html('<br><b>'+ r +'</b>');
    } else {
     dialogbox.html('<br><b>'+ translate('OK') +'</b>');
     setTimeout (dialogbox.destroy, 1000);
    }
   }
  }
  
  function toggleAttackable (event){
   var n = parseInt(event.target.getAttribute('ref'));
   targets[n].at = event.target.checked;
   setButtonStyle ($id(UID['tabAttackTarget_AttackNow_'+n]), targets[n].at);     
  }
  
  function tick (){
   var now = serverTime();
   clearTimeout (timer);
   
   if (!$id(UID['tabAttackTarget_Tab'])){
    return;
   }
   
   for (var i=0; i < targets.length; i++){
    var lastAttack = $id(UID['tabAttackTarget_lastAttack_'+i]);
    if (lastAttack == undefined){
     continue;
    }
    
    var ts;
    if (targets[i].lst == 0){
     ts = '---';
    }
    else {
     var time = now - targets[i].lst;
     // fix this :P
     if (time > 3600){
      ts = '<font color=#550000><b>'+ timeFormat (time, false) +'</b></font>';
     }
     else {
      ts = timeFormat (time, false);
     }
    }
    lastAttack.innerHTML = ts;
   }
   timer = setTimeout (tick, 5000);
  }
 },

 
 //*** Attacks Tab - Stats Sub-tab ***
 //----------------------------------------------------------------------------
 tabAttackStats : function (){
  var t = Tabs.Attacks;

  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabAttackStats']).addClass('selected');
  $J('#'+UID['tabAttackStats']).css('z-index', '1');
  t.lastSubTab = 'tabAttackStats';
  
  t.contentType = 3;

  var html = [
   '<div class=' + UID['title'] + '>' + translate('Statistics') + '</div>'
  ,'<div id='+ setUID('tabAttackStats_Status') + ' class=' + UID['status_ticker'] + '></div>'
  ,'<br/>'
  ,'<center><input id=' + setUID('tabAttackStats_Clear') + ' type=button value="' + translate('Delete') + ' ' + translate('Statistics') +'" /></center>'
  ];
  
  $J('#'+UID['tabAttack_Content']).html( html.join('') );
  
  $J('#'+UID['tabAttackStats_Clear']).click(function(){
   t.clearStats();
   t.showStats();
  });
  
  t.showStats();
 },

 // byLevel.resources
 clearStats : function (){
  var t = Tabs.Attacks;
  var now = serverTime();
  Data.options.objStats = {tsStart:now, runTime:0, numAttacks:0, items:{}, resources:{}, byLevel:[]};
  t.curRunStart = now;
  for (var i=0; i < 12; i++){
   Data.options.objStats.byLevel[i] = {numAttacks:0, items:{}, resources:{}};
  }
  t.showStats(); 
 },
 
 trackStats : function (march_id, rpt){   // called when battle report received
  var t = Tabs.Attacks;
  if (DEBUG_MARCHES){
   WinLog.write ('Tabs.Attacks.trackStats: '+ marchId);
  }
  var objLevel = rpt.report.location.level;
  
  if (objLevel < 1 || objLevel > 12){
   objLevel = 0;
  }
  
  ++Data.options.objStats.numAttacks;
  ++Data.options.objStats.byLevel[objLevel].numAttacks;
  var res =  rpt.report.spoils.resources;
  
  for (var p in res){
   objAddTo (Data.options.objStats.resources, p, parseInt(res[p]));
   objAddTo (Data.options.objStats.byLevel[objLevel].resources, p, parseInt(res[p]));
  }  
  
  var items =  rpt.report.spoils.items;
  for (var i=0; i < items.length; i++){
   objAddTo (Data.options.objStats.items, items[i], 1);
   objAddTo (Data.options.objStats.byLevel[objLevel].items, items[i], 1);
  }  
  
  Marches.remove (march_id, 'attacks');
  t.showStats();
 },

 showStats : function (){
  var t = Tabs.Attacks;
  
  var runTime = Data.options.objStats.runTime;
  if (Data.options.objAttack.enabled){
   runTime += (serverTime()-t.curRunStart);
  }
  
  var trueRunTime = (runTime > 0) ? (runTime/3600) : 1;
  
  var html = [
   '<table class=' + UID['table'] + '>'
  ,' <tr>'
  ,'  <td class=left>' + translate('Start Date') + ': </td>'
  ,'  <td>' + new Date(Data.options.objStats.tsStart * 1000).myString() + '</td>'
  ,' </tr><tr>'
  ,'  <td class=left>' + translate('Run Time') + ': </td>'
  ,'  <td>' + timeFormat(runTime, true) + '</td>'
  ,' </tr><tr>'
  ,'  <td class=left>' + translate('Attacks') + ': </td>'
  ,'  <td>' + Data.options.objStats.numAttacks + '</td>'
  ,' </tr><tr valign=top>'
  ,'  <td class=left>' + translate('Resources') + ': </td>'
  ,'  <td>'
  ,'   <table class=' + UID['table'] + '>'
  ];
  
  for (var name in Data.options.objStats.resources)
  {
   var perHour = Data.options.objStats.resources[name] / trueRunTime;
   html = html.concat([
    ' <tr align=right>'
   ,'   <td>' + translate(name) + ':</td>'
   ,'   <td>' + Data.options.objStats.resources[name].intToCommas() + '</td>'
   ,'   <td>(' + perHour.intToCommas() + ' /' + translate('h') + ')</td>'
   ,'  </tr>'
   ]);
  }
  
  html = html.concat([
   '  </table>'
  ,'  </td>'
  ,'</tr></table>'
  ]);
  
  html = html.concat([
   '<br><div class=' + UID['subtitle'] + '>'+ translate('Statistics') +'&nbsp;'+ translate('of') +'&nbsp;'+ translate('Attack') +' '+ translate('and') +' '+ translate('Items') +'</div>'
  ,'<div style="overflow-y:auto">'
  ,' <table class=' + UID['table'] + '>'
  ,'  <tr class=' + UID['row_top_headers'] + ' align=center>'
  ,'   <td style="background:none !important;"></td>'
  ,'   <td align=right colspan=11>'+ translate('Levels') +'</td>'
  ,'  </tr><tr align=right class=' + UID['row_headers'] + '>'
  ,'   <td style="background:none !important;"></td>'
  ]);
  
  for (i=1; i < 12; i++) {
   html = html.concat(['<td width=45>' + i + '</td>']);
  }
  
  html = html.concat([
   ' </tr><tr>'
  ,'   <td colspan=11><HR class=thin></td>'
  ,'  </tr><tr align=right>'
  ,'   <td class=left># ' + translate('Attacks') + ':</td>'
  ]);
  
  for (i=1; i < 12; i++){
   html = html.concat(['<td>' + Data.options.objStats.byLevel[i].numAttacks + '</td>']);
  }
  
  html = html.concat([
  ' </tr><tr>'
  ,'   <td colspan=11><HR class=thin></td>'
  ,'  </tr>'
  ]);
  
  var items =  flipStats ('items');     
  for (var p in items){
   html = html.concat([
   '<tr align=right>'
   ,'  <td class=left>' + translate(p) + ':</td>'
   ]);
   
   for (i=1; i < 12; i++) {
    html = html.concat(['<td>' + items[p][i] + '</td>']);
   }
  }
  
  html = html.concat(['</tr></table></div>']);
  
  $J('#'+UID['tabAttackStats_Status']).html( html.join('') );
  
  function flipStats (name){
   var o = {};
   for (var i=1; i < 12; i++){
    for (var p in Data.options.objStats.byLevel[i][name]){
     if (!o[p])
     {
      o[p] = [];
      for (var x=1; x < 12; x++){
       o[p][x] = 0;
      }
     }
     o[p][i] += Data.options.objStats.byLevel[i][name][p];
    }
   }
   return o;
  }
 },
 

 
 //*** Attacks Tab - Maps Sub-tab ***
 //----------------------------------------------------------------------------
 tabAttackMaps : function(){
  var t = Tabs.Attacks;

  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabAttackMaps']).addClass('selected');
  $J('#'+UID['tabAttackMaps']).css('z-index', '1');
  t.lastSubTab = 'tabAttackMaps';

  t.contentType = 4;

  var html = [
   '<div class=' + UID['subtitle'] + '>' + translate('Search') + ' ' + translate('Location') + '</div>'
  ,'<div>'
  ,' <b>' + translate('Search Radius') + ':</b> '
  ,' <select id=' + setUID('tabAttackMaps_Radius') + '>'
  ];
  
  for (var i=5; i <= t.maxRadius; i+=5){
   html = html.concat([
   ' <option value="' + i + '" ' + (Data.map.radius == i ? 'selected' : '') + '>' + i + '</option>'
   ]);
  }
  
  html = html.concat([
  ' <select> '
  , translate('miles') + '.&nbsp;&nbsp;'
  ,'<input id=' + setUID('tabAttackMaps_Search') + ' type=button value="' + translate('Search') + '" />'
  ,'<br><br>'
  ,' <table class=' + UID['table'] + '>'
  ,'  <tr align=center class=' + UID['row_headers'] + '>'
  ,'   <td>' + translate('type')  + '</td>'
  ,'   <td>' + translate('total') + '</td>'
  ,'  </tr>'
  ]);
  
  // Add Search Report
  for (var type in Data.map.terrains){
   html = html.concat([
   '<tr>'
   ,' <td>' + translate(type) + '</td>'
   ,' <td align=right>'
   ,'  <span class=jewel>' + Data.map.terrains[type].length + '</span>'
   ,' </td>'
   ,'</tr>'
   ]);
  }

  html = html.concat(['</table></div>']);
  
  // Display the inputs
  $J('#'+UID['tabAttack_Content']).html ( html.join('') );

  // add event listeners
  $J('#'+UID['tabAttackMaps_Search']).click (butSearchNow);
  
  $J('#'+UID['tabAttackMaps_Radius']).change (function(event){
   var element = event.target;
   Data.map.radius = parseInt(element.options[element.selectedIndex].value);
  });
  
  
  // search the map for the selected type
  function butSearchNow (event){
   verboseLog('<b>scanMap</b>: Begin...');

   var t = Tabs.Attacks;

   var dialogbox = dialogBox({
    id   : setUID('dialog-scanmap'),
    minWidth : 300,
    centerTo : t.container,
    overlay  : true,
    title  : 'Scanning Map',
    html  : '<center>' + translate('Scanning Map').replace('$NUM$',Data.map.radius) + '<br><br><div class=progressbar></div></center>'
   });
   
   var x = Data.map.x;
   var y = Data.map.y;
   var radius = Data.map.radius;
   
   Map.scanMap (x,y, radius, function(res){
    if (res == null){
     verboseLog('<b>scanMap</b>: there was an <b>error</b> while scanning the map');
     dialogbox.html('<B>' + translate('Bummer, there was an error while scanning the map') + '.</B>');
     Tabs.Attacks.checkMapBusy = false;
     return;
    }
    if(res.done){
     verboseLog('scanMap: complete!');

     Tabs.Attacks.checkMapBusy = false;
     
     var html = '<center><br>' + translate('complete') + '!<br><br><table>';
     for (var type in res.terrains){
      html += '<tr><td>'+ translate(type) + '&nbsp;</td><td>&nbsp;' + res.terrains[type].length + '</td></tr>';
     }
     html += '</table></center>'
     
     dialogbox.html(html);
     dialogbox.centerTo();
     dialogbox.buttons([
      {
       text: translate("Ok"),
       click: function() { 
        dialogbox.destroy();
       }
      }
     ]);
     
     // Refresh the Tab Content
     t.tabAttackMaps();
     
    } else if(res.init){
     StepTimeBar.start({target:$J('#'+UID['dialog-scanmap']+' .progressbar'), steps:Map.steps});
    }    
    else {
     StepTimeBar.update(Map.step);
    }
   });
   
  }
  
 },
 
  //*** Attacks Tab - Config Sub-Tab ***
 //----------------------------------------------------------------------------
 tabAttackConfig : function (){
  var t = Tabs.Attacks;
  
  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabAttackConfig']).addClass('selected');
  $J('#'+UID['tabAttackConfig']).css('z-index', '1');
  t.lastSubTab = 'tabAttackConfig';

  t.contentType = 1;

  var html = '<div class=' + UID['title'] + '>'+ translate('Attacks Configuration') + '</div>'
  +'<div>'
  +' <table class=' + UID['table'] + '>'
  +' <tr>'
  +'  <td class=left>'+ translate('Delay Between Attacks') +':&nbsp;</td>'
  +'  <td>'
  +'  <input class=short id='+ setUID('tabAttackConfig_DelayMin') +' maxlength=4 type=text value="'+ Data.options.objAttack.delayMin +'" />&nbsp;'+ translate('to')
  +'  <input class=short id='+ setUID('tabAttackConfig_DelayMax') +' maxlength=4 type=text value="'+ Data.options.objAttack.delayMax +'" />&nbsp;'+ translate('Seconds').toLowerCase() 
  +'  </td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Delete') +' '+ translate('Battle Report') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_DelAttacks') +' '+ (Data.options.objAttack.deleteObjAttacks?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Stop if any troops lost') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_StopOnLoss') +' '+ (Data.options.objAttack.stopAttackOnLoss?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Maximum simultaneous marches') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_MaxMarches') +' class=short maxlength=2 type=text value="'+ Data.options.objAttack.maxMarches +'" /></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Enable') +' '+ translate('Attacks Logs') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_LogAttack') +' '+ (Data.options.objAttack.logAttacks?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr><tr>'
  +'  <td colspan=2><hr></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Dont make Wildernesses') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_AbandonWildernesses') +' '+ (Data.options.objAttack.abandonWildernesses?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Re-call troops if encamped') +':&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_RecallEncamped') +' '+ (Data.options.objAttack.recallEncamped?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr><tr>'
  +'  <td colspan=2><hr></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Clear last attack on current map') +'&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_ClearLast') +'  type=button value="'+translate('Delete')+'" /></td>'
  +' </tr><tr>'
  +'  <td class=left>'+ translate('Clear last attack on all maps') +'&nbsp;</td>'
  +'  <td><input id='+ setUID('tabAttackConfig_ClearAll') +' '+ (Data.options.objAttack.clearAllTargets?'CHECKED ':'') +' type=checkbox /></td>'
  +' </tr></table>';
  
  $id(UID['tabAttack_Content']).innerHTML = html;
  
  // Add event listeners
  $J('#'+UID['tabAttackConfig_DelAttacks']).change(function (event){
   Data.options.objAttack.deleteObjAttacks = event.target.checked;
  });
  $J('#'+UID['tabAttackConfig_StopOnLoss']).change(function (event){
   Data.options.objAttack.stopAttackOnLoss = event.target.checked;
  });
  $J('#'+UID['tabAttackConfig_LogAttack']).change(function (event){
   Data.options.objAttack.logAttacks = event.target.checked;
  });
  
  $J('#'+UID['tabAttackConfig_AbandonWildernesses']).change(function (event){
   Data.options.objAttack.abandonWildernesses = event.target.checked;
  });
  $J('#'+UID['tabAttackConfig_RecallEncamped']).change(function (event){
   Data.options.objAttack.recallEncamped = event.target.checked;
  });
  
  $J('#'+UID['tabAttackConfig_ClearAll']).change(function (event){
   Data.options.objAttack.clearAllTargets = event.target.checked;
  });
  $J('#'+UID['tabAttackConfig_DelayMin']).change(delayChanged);
  $J('#'+UID['tabAttackConfig_DelayMax']).change(delayChanged);
  $J('#'+UID['tabAttackConfig_MaxMarches']).change(maxMarchesChanged);
  $J('#'+UID['tabAttackConfig_ClearLast']).click(clearLast);
  
  
  function delayChanged (event){
   var min = parseIntNan($id(UID['tabAttackConfig_DelayMin']).value);
   var max = parseIntNan($id(UID['tabAttackConfig_DelayMax']).value);
   if (min < ATTACK_MIN_DELAY || min > 3600 || (max-min) < 30){
    var dialogbox = dialogBox({
     id  : setUID('dialog-invalid-range'),
     centerTo: t.container,
     title : translate('Invalid delays'),
     html : '<br>'+translate('First value must be between') + ' ' + ATTACK_MIN_DELAY + ' '
       + translate('and') + ' 3600 ' + translate('Seconds') + ' <br>'
       + translate('Second value must be at least') + ' 30 ' + translate('above the first value'),
    });
    
    if (min < ATTACK_MIN_DELAY){
     if(max > min+30){
      min = max - 30;
     } else {
      min = ATTACK_MIN_DELAY;
      max = min + 30;
     }
    } else if (min > 3600) {
     min = 3600;
     max = min + 30;
    } else {
     max = min + 30;
    }
    $id(UID['tabAttackConfig_DelayMin']).value = min;
    $id(UID['tabAttackConfig_DelayMax']).value = max;
   }
   Data.options.objAttack.delayMin = min;
   Data.options.objAttack.delayMax = max;
  }
  
  function maxMarchesChanged (event){
   var val = parseIntNan($id(UID['tabAttackConfig_MaxMarches']).value);
   if (val < 0 || val > 12){
    event.target.style.backgroundColor = 'red';
    return;
   }
   event.target.style.backgroundColor = '';
   Data.options.objAttack.maxMarches = val;
  } 
  
  // Clear the information about when the target was last attacked
  // This is useful because attacks always start with the oldest target or, 
  // if no target has been attacked (last == 0), the first target in the list
  function clearLast (event){
   if (Data.options.objAttack.clearAllTargets) {
    // Make sure the user has scanned the map
    for (var type in Data.map.terrains) {
     for (var i=0; i<Data.map.terrains[type].length; i++) {
      (Data.map.terrains[type])[i].lst = 0;
     }
    }
   }
   else {
    // Clear the last attacked field of the currently selected target
    var targets = Data.map.terrains[Data.map.selected];
    for (var i=0; i < targets.length; i++) {
     targets[i].lst = 0;
    }
   }
  }
 }
 
};
//******************************** End Attacks *************************



/***********************************   Jobs Tab   **********************************/
Tabs.Jobs = {
 tabOrder  : JOBS_TAB_ORDER,
 tabLabel  : 'Tasks',
 tabDisabled  : !JOBS_TAB_ENABLE,
 lastSubTab  : 'tabJobInfo',
 container  : null,
 timer   : null,
 buildStatTimer : null,
 researchStatTimer: null,
 trainTimer  : null,
 trainStatTimer : null,
 
 city: {
  0: {
   units_type : [kPorter, kConscript, kSpy, kHalberdsman, kMinotaur, kLongbowman, kSwiftStrikeDragon, kBattleDragon, kArmoredTransport, kGiant, kFireMirror],
  },
  1:{
   units_type : [],
  },
  2:{
   units_type : [],
  },
  3:{
   units_type : [],
  },
  4:{
   units_type : [],
  },
 },
 selectedQ  : 'min_housing',
 trainJobs  : [],

 researchIdx  : {Agriculture:0, Woodcraft:1, Masonry:2, Mining:3, Clairvoyance:4, RapidDeployment:5, Ballistics:6, Metallurgy:7, Medicine:8, Dragonry:9, Levitation:10, Mercantilism:11, AerialCombat:12},
 
 researchName : [kAgriculture, kWoodcraft, kMasonry, kMining, kClairvoyance, kRapidDeployment, kBallistics, kMetallurgy, kMedicine, kDragonry, kLevitation, kMercantilism, kAerialCombat],
 
 capitolCity  : [kHome, kGarrison, kScienceCenter, kMetalsmith, kOfficerQuarter, kMusterPoint, kRookery, kStorageVault, kTheater, kSentinel, kFactory, kFortress, kDragonKeep, kWall],
 
 capitolField : [kMine, kFarm, kLumbermill, kQuarry],
 outpostCity  : [kTrainingCamp, kHome, kSilo, kMusterPoint, kDragonKeep, kWall],
 outpostField : [kMine, kFarm, kLumbermill, kQuarry],
 contentType  : 0, // 0 = info, 1 = train, 2 = build, 3 = research, these should be enums but Javascript doesn't support that type
 trainContentType: 0, // 0 = train, 1 = config
 buildScrollPos : 0,
 
 init : function (div){
  var t = Tabs.Jobs;
  
  // Variables Initializations
  t.city[1].units_type = t.city[0].units_type.concat([kAquaTroop]);
  t.city[2].units_type = t.city[0].units_type.concat([kStoneTroop]);
  t.city[3].units_type = t.city[0].units_type.concat([kFireTroop]);
  t.city[4].units_type = t.city[0].units_type.concat([kWindTroop]);
  
  t.units_type = t.city[0].units_type.concat([kAquaTroop, kStoneTroop, kFireTroop, kWindTroop]);
  
  
  for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++)
  {
   // Training initialization
   if (!Data.options.autoTrain.city[cityIdx].units) {
    Data.options.autoTrain.city[cityIdx].units = {};
    for (i=0; i < t.city[cityIdx].units_type.length; i++){
     var unit_type = t.city[cityIdx].units_type[i];
     Data.options.autoTrain.city[cityIdx].units[unit_type] = 0;
    }
   }
   
   // Training troopCap initialization
   if (!Data.options.troopCap.city[cityIdx].units) {
    Data.options.troopCap.city[cityIdx].units = {};
    for (i=0; i < t.units_type.length; i++){
     var unit_type = t.city[cityIdx].units_type[i];
     Data.options.troopCap.city[cityIdx].units[unit_type] = 0;
    }
   }
  }
  
  // Restore the views
  t.contentType = Data.options.jobsTab;
  t.trainContentType = Data.options.trainTab;
  
  // Tab initialization
  t.container = div;
  var html = [
  '<ul class=tabs>'
  ,' <li class="tab first"><a id=' + setUID('tabJobInfo') + '>' + translate('Summary')  + '</a></li>'
  ,' <li class=tab><a id=' + setUID('tabJobTrain')        + '>' + translate('Train')    + '</a></li>'
  ,' <li class=tab><a id=' + setUID('tabJobBuild')        + '>' + translate('Build')    + '</a></li>'
  ,' <li class=tab><a id=' + setUID('tabJobResearch')     + '>' + translate('Research') + '</a></li>'
  ,'</ul>'
  ,'<div id=' + setUID('tabJob_Header') + '></div>'
  ,'<div id=' + setUID('tabJob_Content') + '></div>'
  ];
  
  $J( t.container ).html( html.join('') );
  
  // Styles & Sets
  $J('#'+UID['tabJob_Header']).css('height','225px');
  $J('#'+UID['tabJob_Header']).css('margin-bottom','5px');
  
  $J('#'+UID['tabJob_Content']).addClass(UID['content']);
    
  $J('#'+UID['tabJob_Content']).css('height','440px');
  $J('#'+UID['tabJob_Content']).css('padding-top','5px');
  $J('#'+UID['tabJob_Content']).css('padding-bottom','5px');
  
  // Event Listeners
  $J('#'+UID['tabJobInfo']).click (t.tabJobInfo);
  $J('#'+UID['tabJobTrain']).click (t.tabJobTrain); 
  $J('#'+UID['tabJobBuild']).click (t.tabJobBuild);
  $J('#'+UID['tabJobResearch']).click (t.tabJobResearch);
  
  
  
  // Enable the jobs
  t.setTrainEnable (Data.options.autoTrain.enabled); 
  t.selectedQ = Data.options.trainQChoice;
  t.setBuildEnable (Data.options.autoBuild.enabled);
  t.setResearchEnable (Data.options.autoResearch.enabled);
  
  // Add the unload event listener
  window.addEventListener('unload', t.onUnload, false);
  
 },

 show : function (){
  var t = Tabs.Jobs;
  
  switch (t.contentType) {
   case 0: t.tabJobInfo(); break;
   case 1: t.tabJobTrain(); break;
   case 2: t.tabJobBuild(); break;
   case 3: t.tabJobResearch(); break;
  }
 },
 
 hide : function (){
  var t = Tabs.Jobs;
  //t.clearTimers();
 },
 
 onUnload : function () {
  logit('Tabs.Jobs.onUnload');
  var t = Tabs.Jobs;
  Data.options.jobsTab = t.contentType;
  Data.options.trainTab = t.trainContentType;
  Data.options.trainQChoice = t.selectedQ;
 },

 clearTimers : function (){
  var t = Tabs.Jobs;
  clearTimeout (t.jobsStatTimer);
  clearTimeout (t.trainStatTimer);
  clearTimeout (t.buildStatTimer);
  clearTimeout (t.researchStatTimer);
 },
 
 // ** Tab: Jobs - SubTab:  Info
 //--------------------------------------------------------------------
 tabJobInfo : function (){
  var t = Tabs.Jobs;
  $J( '#' + UID[t.lastSubTab] ).removeClass('selected');
  $J( '#' + UID[t.lastSubTab] ).css('z-index','0');
  $J( '#' + UID['tabJobInfo'] ).addClass('selected');
  $J( '#' + UID['tabJobInfo'] ).css('z-index','1');
  t.lastSubTab = 'tabJobInfo';
  
  t.contentType = 0;
  
  var city = Seed.cities[0];
  
  $J( '#'+UID['tabJob_Header'] ).html( '<div>' + translate('Info') + '</div>' );
  $J( '#'+UID['tabJob_Header'] + ' > div' ).addClass(UID['title']);
  
  $J( '#'+UID['tabJob_Header'] ).css('height', '12pt');
  
  
  $J( '#'+UID['tabJob_Content'] ).css('height', '650px');
  $J( '#'+UID['tabJob_Content'] ).html('<div id="' + setUID('tabJob_Container') + '"></div>'); 

  var $container = $J( '#'+UID['tabJob_Container'] );
  
  function jobsStatTick(){
   var html = [];
   // Capital & Outposts ...
   for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++){
    
    html = html.concat([
      cityTitle(cityIdx) 
    ,'<table class=' + UID['table'] + '>' 
    ,   dispBuildingJob(cityIdx)
    ]);
    
    if (cityIdx == 0){
     html = html.concat([
     ,' <tr><td>&nbsp;</td></tr>'
     ,   dispResearchJob(0)
     ]);
    }
    
    html = html.concat([
     ' <tr><td>&nbsp;</td></tr>'
    ,   dispTrainingJobs(cityIdx) 
    ,' </td></tr>'
    ,'</table>'
    ]);
   }
   
   $container.html( html.join('') );
   $container.css('height' , $J('#'+UID['tabJob_Content']).innerHeight()-10 + 'px');
   $container.addClass(UID['scrollable']);
  }
  
  
  // Display build queue
  function dispBuildingJob (cityIdx){
   var html = [
    '<tr>'
   ,' <td class=left><b>'+ translate('Building') +':</b> </td>'
   ];
   var job = getBuildingJob (cityIdx);
   
   // TODO: very rare occurance: Error: job.building is null
   if (job && job.job.run_at > serverTime())
   {
    // Don't show negative values - not pleasant user interface. To be truly nice, if the time is less than zero, we should reset 
    // the build timer. For now, that is done by the Build tab's notification process
    html = html.concat([
     ' <td align=right>' + translate(job.building.type) + ' (' + job.job.level + ') &nbsp;</td>'
    ,' <td>&nbsp;<font color=' + TIMER_COLOR + '>' + timeFormat(job.job.run_at - serverTime(), true) + '</font></td>'
    ,'</tr>'
    ]);
   }
   else {
    html = html.concat([
     ' <td align=right colspan=2>'
    ,'  <span class=' + UID['bold_red'] + '>' + translate('None').toUpperCase() + '</span>'
    ,' </td>'
    ,'</tr>'
    ]);
   }
   return html.join('');
  }
  
  // Display research queue
  function dispResearchJob (cityIdx){
   var html = [
    '<tr>'
   ,' <td class=left><b>'+ translate('Researching') +': </b></td>'
   ];
    
   var job = getResearchJob (cityIdx);
   if (job && job.run_at > serverTime())
   {
    html = html.concat([
     ' <td align=right>' + translate(job.research_type) + ' (' + job.level + ') &nbsp;</td>'
    ,' <td>&nbsp;<font color=' + TIMER_COLOR + '>' + timeFormat(job.run_at - serverTime(), true) + '</font></td>'
    ,'</tr>'
    ]);
   } else {
    html = html.concat([
     ' <td align=right colspan=2>'
    ,'  <span class=' + UID['bold_red'] + '>' + translate('None').toUpperCase() + '</span>'
    ,' </td>'
    ,'</tr>'
    ]);
   }
   return html.join('');
  }
  
  // Display training queues
  function dispTrainingJobs (cityIdx){
   var html = [], last = serverTime(), trains = [];
   
   for (var i=0; i < Seed.cities[cityIdx].jobs.length; i++){
    if (Seed.cities[cityIdx].jobs[i].queue=='units' && 
     Seed.cities[cityIdx].jobs[i].unit_type && 
     Seed.cities[cityIdx].jobs[i].run_at > last
     ){
      trains.push (Seed.cities[cityIdx].jobs[i]);
    }
   }
   trains.sort(function(a,b){return a.run_at-b.run_at});
   
   for (var i=0; i < trains.length; i++){
    var left='', tot='', timeRemaining = 0;
    if (i==0){
     left = translate('Training') + ':';
    }
    else if (i==trains.length-1) {
     timeRemaining = (trains[i].run_at-serverTime() > 0) ? trains[i].run_at-serverTime() : 0;
     tot = '&nbsp;<b>(' + timeFormatShort(timeRemaining) + ')</b>';
    }
    
    timeRemaining = (trains[i].run_at - last > 0) ? trains[i].run_at - last : 0;
    html = html.concat([
     '<tr>'
    ,' <td class=left><b>' + left + '</b>&nbsp;</td>'
    ,' <td align=right>' + trains[i].quantity + '&nbsp;&nbsp;' + translate(trains[i].unit_type) + '&nbsp;</td>'
    ,' <td>&nbsp;'
    ,'  <font color=' + TIMER_COLOR + '>'
    ,   timeFormat(timeRemaining, true) + tot 
    ,'  </font>'
    ,' </td>'
    ,'</tr>'
    ]);
    last = trains[i].run_at;
   }      
   return html.join('');
  }
  
  function cityTitle (cityIdx){
   var city = Seed.cities[cityIdx];
   // Outposts are always defending (until further notice)
   var wallStatus = '';
   var alliance_name = (Seed.player.alliance) ? Seed.player.alliance.name : '';
   //alliance_name = (city.type == kOutpost) ? '' : alliance_name;
   
   if (cityIdx == 0){
    if ( Seed.cities[cityIdx].defended ){
     wallStatus = '<font class=' + UID['defending'] + '>' + translate('Defend').toUpperCase() + '</font>';
    } else {
     wallStatus = '<font class=' + UID['hiding']    + '>' + translate('Hiding').toUpperCase() + '</font>';
    }
   }
   else {
    wallStatus = '<font class=' + UID['defending'] + '>' + translate('Defend').toUpperCase() + '</font>';
   }
   var html = [
    '<div class=' + UID['subtitle'] + '>'
   ,' <table class=' + UID['table'] + '>'
   ,' <tr>'
   ,'  <td align=left>' + city.name + '</td>'
   ,'  <td align=center>' + city.x + ',' + city.y + '</td>'
   ,'  <td align=center width=200px>'
   ,'   <font color=yellow>' + (cityIdx == 0 ? alliance_name : '') + '</font>'
   ,'  </td>'
   ,'  <td width=80px align=right>' + wallStatus + '</td>'
   ,' </tr>'
   ,' </table>'
   ,'</div>'
   ];
   return html.join('');
  }
  
  // First run of jobsStatTick
  jobsStatTick();
  
  // Timers
  t.clearTimers();
  t.jobsStatTimer = setInterval (jobsStatTick, 1000);
 },

 
 //----------------------------------------------------------------------------
 //*** Jobs Tab - Train Sub-tab ***
 //----------------------------------------------------------------------------
 tabJobTrain : function (){
  var t = Tabs.Jobs;
  $J( '#' + UID[t.lastSubTab] ).removeClass('selected');
  $J( '#' + UID[t.lastSubTab] ).css('z-index','0');
  $J( '#' + UID['tabJobTrain'] ).addClass('selected');
  $J( '#' + UID['tabJobTrain'] ).css('z-index','1');
  t.lastSubTab = 'tabJobTrain';
  
  t.contentType = 1;
  
  // Create status ticker
  var header = [
   '<div class=' + UID['title'] + '>' + translate('Training Progress') + '</div>'
  ,'<div class=' + UID['status_ticker'] + ' style="margin-bottom: 5px !important">'
  ,' <center>'
  ,'  <input id=' + setUID('tabJobTrain_OnOff') + ' type=button />'
  ,' </center>'
  ,' <div id=' + setUID('tabJobTrain_Report') + ' class='+ UID['status_report'] + '></div>'
  ,' <br>'
  ,' <div id='+ setUID('tabJobTrain_Feedback') +' class='+ UID['status_feedback'] + '></div>'
  ,'</div>'
  ,'<ul class=tabs>'
  ,' <li class="tab first"><a id=' + setUID('tabJobTrain_tabTrain')  + '>' + translate('Train')  + '</a></li>'
  ,' <li class="tab"><a id='       + setUID('tabJobTrain_tabConfig') + '>' + translate('Config') + '</a></li>'
  ,'</ul>'
  ];
  
  // Styles & Sets
  $J('#'+UID['tabJob_Header']).html( header.join('') );
  $J('#'+UID['tabJob_Header']).css('height', '225px');
  
  $J('#'+UID['tabJob_Content']).html('<div id=' + setUID('tabJobTrain_Content') + '>');
  $J('#'+UID['tabJob_Content']).css('height', '440px');
  $J('#'+UID['tabJob_Content']).css('margin-top','17px');
  
  $J('#'+UID['tabJobTrain_Content']).css('height' , ($J('#'+UID['tabJob_Content']).innerHeight()-10) + 'px');
  $J('#'+UID['tabJobTrain_Content']).addClass(UID['scrollable']);
  
  // Add event listener for Enabled/off button
  $J('#'+UID['tabJobTrain_OnOff']).click (function (){
   var t=Tabs.Jobs;
   t.setTrainEnable (!Data.options.autoTrain.enabled);
  });
  $J('#'+UID['tabJobTrain_tabTrain']).click (t.tabJobTrainSets);
  $J('#'+UID['tabJobTrain_tabConfig']).click (t.tabJobTrainConfig);
  t.refreshTrainButton (Data.options.autoTrain.enabled);
  
  switch (t.trainContentType) {
   case 0: t.tabJobTrainSets(); break;
   case 1: t.tabJobTrainConfig(); break;
  }
  
  //First Run of trainStatTick
  t.trainStatTick();
  
  // Timers
  t.clearTimers();
  t.trainStatTimer = setInterval(t.trainStatTick, 1000);
 },

 
 //*** Jobs Tab - Train Sub-tab  - Train Sub-Sub-tab ***
 //----------------------------------------------------------------------------
 tabJobTrainSets : function(){
  var t = Tabs.Jobs;
  // Hilite the sub-tabs correctly
  $J('#'+UID['tabJobTrain_tabConfig']).removeClass('selected');
  $J('#'+UID['tabJobTrain_tabConfig']).css('z-index', '0');
  $J('#'+UID['tabJobTrain_tabTrain']).addClass('selected');
  $J('#'+UID['tabJobTrain_tabTrain']).css('z-index', '1');
  t.trainContentType = 0;
  
  // Create troop table for each city
  var elements_id = [], html = [];
  
  for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++){
  
   var city = Seed.cities[cityIdx];
   html = html.concat([
    '<div class=' + UID['subtitle'] + '>'+ city.name +'</div>'
   ,'<table class=' + UID['table'] + '>'
   ,' <tr valign=top>'
   ,'  <td width=150>'
   ,'  <table class=' + UID['table'] + '>'
   ]);

   html = html.concat([
    ' <tr class=' + UID['row_headers'] + '>'
   ,'  <td></td>'
   ,'  <td>' + translate('Quantity') + '</td>'
   ,'  <td>' + translate('Total') + '</td>'
   ,' </tr>'
   ]);
   
   for (i=0; i < t.city[cityIdx].units_type.length; i++){
    
    var unit_type = t.city[cityIdx].units_type[i];
    
    html = html.concat([
     '<tr>'
    ,' <td class=left>' + translate(unit_type) + ':</td>'
    ]);
    
    var num = Data.options.autoTrain.city[cityIdx].units[unit_type];
    if (!num || isNaN(num)){
     num = 0;
    }
    
    html = html.concat([
     ' <td align=middle>'
    ,' <input type=text id=' + setUID('tabTrain_Troop_' + cityIdx + '_' + i) + ' ref=' + cityIdx + '_' + i +' maxlength=6 size=4 value="' + num + '" style="text-align:right;" />'
    ,' </td>'
    ,' <td align=right>&nbsp;'
    ,'  <span class=jewel>(' + getTroopNumbers(Seed.cities[0], unit_type).total + ')</span>'
    ,' </td>'
    ,'</tr>'
    ]);
   
    elements_id.push(UID['tabTrain_Troop_'+ cityIdx +'_'+ i]);
   
   }
   
   html = html.concat([
   '</table></td></tr></table>'
   ]);
  }    
  
  html = html.concat(['</div>']);
  
  $J('#'+UID['tabJobTrain_Content']).html( html.join('') );

  
  // Update troops on change
  function onChangeUnits (event){
   var args = event.target.getAttribute('ref').split('_');
   var x = parseIntZero(event.target.value);
   var lvl = getMusterPointLevel(0);
   var maxLvl = lvl * 10000;
   maxLvl = (lvl == 11) ? 120000 : maxLvl;
   if (isNaN(x) || x < 0 || x > maxLvl){
    event.target.style.backgroundColor = 'red';
    dialogError (translate('Invalid number of troops',t.container));
   } 
   else {
    event.target.value = x;
    var unit_type = t.city[cityIdx].units_type[args[1]];
    Data.options.autoTrain.city[args[0]].units[unit_type] = x;
    event.target.style.backgroundColor = '';
   }
  }
  
  // Add event listeners for troop quantities 
  for (var i=0; i < elements_id.length; i++){
   $J('#'+elements_id[i]).change(onChangeUnits);
  }
  
 }, 
 
 // config sub tab
 tabJobTrainConfig : function(){
  var t = Tabs.Jobs;
  
  // Hilite the sub-tabs correctly
  $J('#'+UID['tabJobTrain_tabTrain']).removeClass('selected');
  $J('#'+UID['tabJobTrain_tabTrain']).css('z-index', '0');
  $J('#'+UID['tabJobTrain_tabConfig']).addClass('selected');
  $J('#'+UID['tabJobTrain_tabConfig']).css('z-index', '1');
  t.trainContentType = 1;
  
  var html = [
   '<div class=' + UID['subtitle'] + '>' + translate('Training Configuration') + '</div>'
  ,' <div>'
  ,'  <table class=' + UID['table'] + '>'
  ,'  <tr align=center class=' + UID['row_headers'] + '>'
  ,'   <td style="background:none !important;" colspan=2></td>'
  ,'  </tr>'
  ];
  
  // Add the radio buttons 
  setUID('tabTrainConfig_QRadio');
  html = html.concat([
   ' <tr>'
  ,'  <td>'
  ,'  <label>'
  ,'  <input type=radio name=' + UID['tabTrainConfig_QRadio'] + ' value="min_housing" />' + translate('Minimum Housing') 
  ,'  </label>'
  ,'  </td>'
  ,' </tr>'
  ,' <tr>'
  ,'  <td>'
  ,'  <label>'
  ,'  <input type=radio name=' + UID['tabTrainConfig_QRadio'] + ' value="min_resource" />'+ translate('Minimum Resource Levels')
  ,'  </label>'
  ,'  </td>'
  ,' </tr>'
  ,' </table>'
  ,'</div>'
  ]); 
  
  // Create an all troop table
  var elements_id = [];

  html = html.concat([
  '<div class=' + UID['subtitle'] + ' style="background-color:#0044a0;">' + translate('Maximum Troops') + ' (0 = no max)</div>'
  ,' <table class=' + UID['table'] + '>'
  ,'  <tr valign=top>'
  ,'   <td width=150>'
  ,'   <table class=' + UID['table'] + '>'
  ]);
  
  var i;
  for (i=0; i < t.units_type.length; i++){
   html = html.concat([
    '  <tr>'
   ,'   <td class=left>' + translate(t.units_type[i]) + ':</td>'
   ]);
   
   var num = Data.options.troopCap.city[0].units[i];
   if (!num || isNaN(num)){
    num = 0;
   }
   
   html = html.concat([
    '<td>'
   ,'<input type=text id=' + setUID('tabTrainConfig_Cap_' + 0 + '_' + i) + ' ref=' + (0 + '_' + i) + ' maxlength=6 size=2 value="' + num + '" style="text-align:right;" />'
   ,'</td>'
   ,'</tr>'
   ]);

   elements_id.push(UID['tabTrainConfig_Cap_'+ 0 +'_'+ i]);
  }
  
  html = html.concat([
  '   </table>'
  ,'   </td>'
  ,'  </tr>'
  ,' </table>'
  ,'</div>'
  ]);
  
  // Display the page
  $J('#'+UID['tabJobTrain_Content']).html( html.join('') );

  // add event listeners for the radio buttons
  var r = document.getElementsByName(UID['tabTrainConfig_QRadio']);
  for (var i=0; i < r.length; i++)
  {
   $J(r[i]).change(enableChanged);
   // Select the radio button that was last selected
   r[i].checked = (r[i].value == Data.options.trainQChoice);
  }

  // Add event listeners for troop quantities 
  for (var i=0; i < elements_id.length; i++) {
   $J('#'+elements_id[i]).change(troopsChanged);
  }

  // radio buttons are weird    
  function enableChanged(event){
   var t = Tabs.Jobs;
   
   if (Data.options.autoTrain.enabled) {
    t.setTrainEnable(false); // It would be very bad to leave training on when switching queue types. 
    if (t.contentType == 1){
     t.dispFeedback (translate('Safe Mode') +' '+ translate('Training') +' '+ translate('Disabled'));
    }
   }
   
   t.selectedQ = event.target.value;
   Data.options.trainQChoice = event.target.value;
  }
  
  // Update troops on change
  function troopsChanged (event){
   var args = event.target.getAttribute('ref').split('_');
   var x = parseIntZero(event.target.value);
   
   // The upper limit is not important because we are looking at a maximum number of troops
   if (isNaN(x) || x < 0){
    event.target.style.backgroundColor = 'red';
    dialogError (translate('Invalid number of troops',t.container));
   } 
   else {
    event.target.value = x;
    Data.options.troopCap.city[args[0]].units[args[1]] = x;
    event.target.style.backgroundColor = '';
   }
  }
 },
 
 /*
 refresh : function (){
  var t = Tabs.Jobs;
  Seed.fetchPlayer (t.showStuff());  
 },
 */

 //----------------------------------------------------------------------------
 //*** Jobs Tab - Build Sub-tab ***
 //----------------------------------------------------------------------------
 tabJobBuild : function (){
  var t = Tabs.Jobs;
  // Hilite the sub-tabs correctly
  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabJobBuild']).addClass('selected');
  $J('#'+UID['tabJobBuild']).css('z-index', '1');
  t.lastSubTab = 'tabJobBuild';
  
  t.contentType = 2;
  
  var header = [
   '<div class=' + UID['title'] + '>' + translate('Construction Progress') + '</div>'
  ,'<div class=' + UID['status_ticker'] + '>'
  ,' <center>'
  ,'  <input id=' + setUID('tabJobBuild_OnOff') + ' type=button />'
  ,' </center>'
  ,' <div id=' + setUID('tabJobBuild_Report')   + ' class=' + UID['status_report']   + '></div>'
  ,' <br>'
  ,' <div id=' + setUID('tabJobBuild_Feedback') + ' class=' + UID['status_feedback'] + '></div>'
  ,'</div>'
  ];
  
  $J('#'+UID['tabJob_Header']).html( header.join('') );
  $J('#'+UID['tabJob_Header']).css('height', '205px');
  
  $J('#'+UID['tabJob_Content']).html( '<div id=' + setUID('tabJobBuild_Content') + '>' );
  $J('#'+UID['tabJob_Content']).css('height', '460px');
  
  html = [];
  
  var elements_id = [], listC = [], listF = [];

  for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++)
  {
   if (cityIdx == 0){
    listC = t.capitolCity;
    listF = t.capitolField;
   } 
   else {
    listC = t.outpostCity;
    listF = t.outpostField;
   }
   
   // The seed object contains a wealth of information including alliance membership, number of people in the alliance, facebook ids of each member,
   // the ol's information (in alliances and alliance_membership), the s object contains all the buildings for the cities, whether or not the city is
   // on defense, the list of generals, what and where the dragon is, a list of jobs (e.g. research, building, troops training and pending training, current marches)
   // the marches alone say where the troops are, whether or not they are returning or attacking, general assigned, etc.
   
   var city = Seed.cities[cityIdx];
   html = html.concat([
    '<div style="margin-bottom:5px;">'
   ,'<div class=' + UID['subtitle'] + '>' + translate('City') + '&nbsp;' + (cityIdx + 1) + 'º (' + city.type + ')</div>'
   ,'<table class=' + UID['table'] + '>'
   ]);
   
   for (var i=0; i < listF.length; i++)
   {
    html = html.concat([
    ' <tr>'
    ,'  <td>'
    ,'  <label>'
    ,'  <input type=checkbox id=' + setUID('tabJobBuild_CB_' + (cityIdx + '_' + listF[i])) + ' ref='+ (cityIdx + '_' + listF[i]) + ' ' + (Data.options.autoBuild.buildingEnable[cityIdx][listF[i]]?'checked':'') + ' /> '+ translate(listF[i])
    ,'  </label>'
    ,'  <td>&nbsp;'
    ,'  <span class=jewel>' + Buildings.getLevel(cityIdx, listF[i]) + '</span>'
    ,'  </td>'
    ,'  </td>'
    ,'  <td>' + buildDisplayCap(cityIdx,(listC.length + i),listF[i]) +'</td>'
    ,' </tr>'
    ]);
    elements_id.push(UID['tabJobBuild_CB_'+ (cityIdx +'_'+ listF[i])]);
   }
   
   html = html.concat([
   ' <tr>'
   ,'  <td colspan=5><hr></td>'
   ,' </tr>'
   ]);
   
   for (var i=0; i < listC.length; i++)
   {
    html = html.concat([
    ' <tr>'
    ,'  <td>'
    ,'  <label>'
    ,'  <input type=checkbox id=' + setUID('tabJobBuild_CB_' + (cityIdx + '_' + listC[i])) + ' ref=' + (cityIdx + '_' + listC[i]) + ' ' + (Data.options.autoBuild.buildingEnable[cityIdx][listC[i]]?'checked':'') + ' /> ' + translate(listC[i]) 
    ,'  </label>'
    ,'  </td>'
    ,'  <td>&nbsp;'
    ,'  <span class=jewel>' + Buildings.getLevel(cityIdx, listC[i]) + '</span>'
    ,'  </td>'
    ,'  <td>' + buildDisplayCap(cityIdx,i,listC[i]) + '</td>'
    ,' </tr>'
    ]);
    elements_id.push(UID['tabJobBuild_CB_'+ (cityIdx +'_'+ listC[i])]);
   }
   
   html = html.concat(['</table></div>']);
  }    
  html = html.concat(['</div>']);
  
  var $container = $J('#'+UID['tabJobBuild_Content']);
  
  $container.css('height', ($J('#'+UID['tabJob_Content']).innerHeight()-10) + 'px');
  $container.addClass(UID['scrollable']);
  $container.html( html.join('') );
  
  // Add the event listeners for each city's building types
  for (var i=0; i < elements_id.length; i++) {
   $J('#'+elements_id[i]).click(checkedBuild);
  }
  
  // Add the event listeners for each city's building type caps
  // And restore the persistent data since it has to be done in the same loop
  for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++)
  {
   var buildList = (cityIdx==0) ? t.capitolCity.concat(t.capitolField) : t.outpostCity.concat(t.outpostField);
   for (var i=0; i < buildList.length; i++)
   {
    var selectMenu = $id(UID['tabJobBuild_Cap_'+ cityIdx + '_' + buildList[i]]);
    try {
     if (!Data.options.autoBuild.buildCap[cityIdx][i]) {
      var lowestBuildingLevel = t.getCurrentLowestBuildingLevel(cityIdx, buildList[i]);
      selectMenu.selectedIndex = lowestBuildingLevel;
      Data.options.autoBuild.buildCap[cityIdx][i] = lowestBuildingLevel;
     }
     else {
      selectMenu.selectedIndex = Data.options.autoBuild.buildCap[cityIdx][i];
      selectMenu.options[Data.options.autoBuild.buildCap[cityIdx][i]].selected = true;
      if(Data.options.autoBuild.buildingEnable[cityIdx][buildList[i]]){
       t.checkBuildReqs(cityIdx, buildList[i]);
      }
     }
    }
    catch (e) {
    }
    $J(selectMenu).change(changeBuildCap);
   }
  }
  
  // Add the event listeners for the auto-build button and scrollbar
  $J('#'+UID['tabJobBuild_OnOff']).click (function (){
   var t=Tabs.Jobs;
   t.setBuildEnable (!Data.options.autoBuild.enabled);
  });
  t.refreshBuildButton (Data.options.autoBuild.enabled);
  
  function checkedBuild (event){
   var ref = event.target.getAttribute('ref');
   var idx = ref.split ('_');
   var cityId = Seed.cities[idx[0]].id;
   Data.options.autoBuild.buildingEnable[idx[0]][idx[1]] = event.target.checked;
   
   if(Data.options.autoBuild.buildingEnable[idx[0]][idx[1]]){
    t.checkBuildReqs(idx[0], idx[1]);
   }
   
   if (Data.options.autoBuild.enabled && event.target.checked){
    t.buildTick();
   }
  }

  function buildDisplayCap (cityIdx, listIdx, type){
   var minLvl = Buildings.getLevel(cityIdx, type);
   var html = [
    '<td>'
   ,' <select id=' + setUID('tabJobBuild_Cap_' + cityIdx + '_' + type) + ' ref=' + (cityIdx + '_' + listIdx) + '>'
   ,'  <option value=0' + ( minLvl>0  ?' style="display:none;"' : '') + '>0</option>'
   ,'  <option value=1' + ( minLvl>1  ?' style="display:none;"' : '') + '>1</option>'
   ,'  <option value=2' + ( minLvl>2  ?' style="display:none;"' : '') + '>2</option>'
   ,'  <option value=3' + ( minLvl>3  ?' style="display:none;"' : '') + '>3</option>'
   ,'  <option value=4' + ( minLvl>4  ?' style="display:none;"' : '') + '>4</option>'
   ,'  <option value=5' + ( minLvl>5  ?' style="display:none;"' : '') + '>5</option>'
   ,'  <option value=6' + ( minLvl>6  ?' style="display:none;"' : '') + '>6</option>'
   ,'  <option value=7' + ( minLvl>7  ?' style="display:none;"' : '') + '>7</option>'
   ,'  <option value=8' + ( minLvl>8  ?' style="display:none;"' : '') + '>8</option>'
   ,'  <option value=9' + ( minLvl>9  ?' style="display:none;"' : '') + '>9</option>'
   ,'  <option value=10'+ ( minLvl>10 ?' style="display:none;"' : '') + '>10</option>'
   ,'  <option value=11'+ ( minLvl>11 ?' style="display:none;"' : '') + '>11</option>'
   ,' </select></td>'
   ,'  <td id='+ setUID('tabJobBuild_FB_'+cityIdx +'_'+ type) +' class=jewel valign=top style="width:250px;white-space:normal;"></td>'
   ];
   return html.join('');
  }

  // Add to persistent storage
  function changeBuildCap (event) {
   var ref = event.target.getAttribute('ref');
   var idx = ref.split ('_');
   Data.options.autoBuild.buildCap[idx[0]][idx[1]] = event.target[event.target.selectedIndex].value;
   event.target.style.backgroundColor = ''; 
   
   if(Data.options.autoBuild.buildingEnable[idx[0]][idx[1]]){
    t.checkBuildReqs(idx[0], idx[1]);
   }
   
   if (Data.options.autoBuild.enabled){
    t.buildTick();
   }
  }
  
  function onScroll (e){
   if (t.contentType == 2){
    t.buildScrollPos = $id(UID['tabJob_Content']).scrollTop;
   }
  }
  
  // First Run of buildStatTick
  t.buildStatTick();
  
  // Timers
  t.clearTimers();
  t.buildStatTimer = setInterval (t.buildStatTick, 1000); // start the build statistics timer
 },

 
 //----------------------------------------------------------------------------
 //*** Jobs Tab - Research Sub-tab ***
 //----------------------------------------------------------------------------
 tabJobResearch : function (){
  var t = Tabs.Jobs;
  $J('#'+UID[t.lastSubTab]).removeClass('selected');
  $J('#'+UID[t.lastSubTab]).css('z-index', '0');
  $J('#'+UID['tabJobResearch']).addClass('selected');
  $J('#'+UID['tabJobResearch']).css('z-index', '1');
  t.lastSubTab = 'tabJobResearch';
  
  t.contentType = 3;
  
  var header = [
   '<div class=' + UID['title'] + '>' + translate('Research Progress') + '</div>'
  ,'<div class=' + UID['status_ticker'] + '>'
  ,' <center>'
  ,'  <input id=' + setUID('tabJobResearch_OnOff') + ' type=button />'
  ,' </center>'
  ,' <div id=' + setUID('tabJobResearch_Report')   + ' class=' + UID['status_report']   + '></div>'
  ,' <br>'
  ,' <div id=' + setUID('tabJobResearch_Feedback') + ' class=' + UID['status_feedback'] + '></div>'
  ,'</div>'
  ];
  
  $J('#'+UID['tabJob_Header']).css('height', '205px');
  $J('#'+UID['tabJob_Header']).html( header.join('') );
  
  var html = [
  '<div id=' + setUID('tabJobResearch_Config') + '>'
  ];
  
  var city = Seed.cities[0];
  
  html = html.concat([
   '<div class=' + UID['subtitle'] + '>' + translate('City') + ' 1º (' + city.type + ')</div>'
  ,'<table class=' + UID['table'] + '>'
  ]);
  
  var elements_id = [];
  for (var i=0; i < t.researchName.length; i++) {

   var research_type = t.researchName[i];
   var curLvl = Seed.player.research[research_type] || 0;
   
   html = html.concat([
    ' <tr>'
   ,'   <td>'
   ,'   <label>'
   ,'   <input type=checkbox id=' + setUID('tabJobResearch_CB_'+research_type) + ' ' + (Data.options.autoResearch.researchEnable[research_type] ? 'checked' : '') + ' ref=' + i + ' /> ' + translate(research_type) 
   ,'   </label>'
   ,'   <td>'
   ,'    <span class=jewel>' + curLvl + '</span>'
   ,'   </td>'
   ,'   </td>'
   ,'   <td>'
   ,'   <select id=' + setUID('tabJobResearch_Sel_' + research_type) + ' ref=' + i + '>'
   ,'    <option value=0' + ( curLvl>0  ?' style="display:none;"' : '') + '>0</option>'
   ,'    <option value=1' + ( curLvl>1  ?' style="display:none;"' : '') + '>1</option>'
   ,'    <option value=2' + ( curLvl>2  ?' style="display:none;"' : '') + '>2</option>'
   ,'    <option value=3' + ( curLvl>3  ?' style="display:none;"' : '') + '>3</option>'
   ,'    <option value=4' + ( curLvl>4  ?' style="display:none;"' : '') + '>4</option>'
   ,'    <option value=5' + ( curLvl>5  ?' style="display:none;"' : '') + '>5</option>'
   ,'    <option value=6' + ( curLvl>6  ?' style="display:none;"' : '') + '>6</option>'
   ,'    <option value=7' + ( curLvl>7  ?' style="display:none;"' : '') + '>7</option>'
   ,'    <option value=8' + ( curLvl>8  ?' style="display:none;"' : '') + '>8</option>'
   ,'    <option value=9' + ( curLvl>9  ?' style="display:none;"' : '') + '>9</option>'
   ,'    <option value=10'+ ( curLvl>10 ?' style="display:none;"' : '') + '>10</option>'
   ,'    <option value=11'+ ( curLvl>11 ?' style="display:none;"' : '') + '>11</option>'
   ,'   </select>'
   ,'   </td>'
   ,'   <td id=' + setUID('tabJobResearch_FB_' + research_type) + ' class=jewel valign=top style="width:250px;white-space:normal;"></td>'
   ,'  </tr>'
   ]);
   elements_id.push(UID['tabJobResearch_CB_'+research_type]);
  }
  
  html = html.concat(['</table></div>']);
  
  $J('#'+UID['tabJob_Content']).html( html.join('') );
  $J('#'+UID['tabJob_Content']).css('height', '460px');
  
  
  // Add the event listeners for the research types
  for (var i=0; i < elements_id.length; i++){
   $J('#'+elements_id[i]).click(checkedResearch);
  }
  
  // Add the event listeners for the research caps
  // And restore the persistent data since it has to be done in the same loop
  for (var i=0; i < t.researchName.length; i++) {
   var research_type = t.researchName[i];
   var selectMenu = $id(UID['tabJobResearch_Sel_' + research_type]);
   try {
    if (!Data.options.autoResearch.researchCap[research_type]) {
     var currentResearchLevel = t.getCurrentResearchLevel(research_type);
     selectMenu.selectedIndex = currentResearchLevel;
     Data.options.autoResearch.researchCap[research_type] = currentResearchLevel;
    }
    else {
     selectMenu.selectedIndex = Data.options.autoResearch.researchCap[research_type];
     selectMenu.options[Data.options.autoResearch.researchCap[research_type]].selected = true;
     if(Data.options.autoResearch.researchEnable[research_type]){
      t.checkResearchReqs(id);
     }
    }
   }
   catch (e) {
   }
   $J(selectMenu).change(changeResearchCap);
  }
  
  $J('#'+UID['tabJobResearch_OnOff']).click (function (){
   var t=Tabs.Jobs;
   t.setResearchEnable (!Data.options.autoResearch.enabled);
  });
  t.refreshResearchButton (Data.options.autoResearch.enabled);
  
  function checkedResearch (event){
   var t = Tabs.Jobs;
   var n = parseInt(event.target.getAttribute('ref'));
   Data.options.autoResearch.researchEnable[t.researchName[n]] = event.target.checked;
   
   if(Data.options.autoResearch.researchEnable[t.researchName[n]]){
    t.checkResearchReqs(t.researchName[n]);
   }
   
   if (Data.options.autoResearch.enabled){
    t.researchTick();
   }
   
  }


  // Add to persistent storage
  function changeResearchCap (event) {
   var t = Tabs.Jobs;
   var n = parseInt(event.target.getAttribute('ref'));
   Data.options.autoResearch.researchCap[t.researchName[n]] = event.target[event.target.selectedIndex].value;
   event.target.style.backgroundColor = '';  
   
   if(Data.options.autoResearch.researchEnable[t.researchName[n]]){
    t.checkResearchReqs(t.researchName[n]);
   }
   
   if (Data.options.autoResearch.enabled){
    t.researchTick();
   }
  }
  
  // First Run of researchStatTick
  t.researchStatTick();
  
  // Timers
  t.clearTimers();
  t.researchStatTimer = setInterval (t.researchStatTick, 1000); // start the research statistics timer
 },

 setTrainEnable : function (onOff){
  var t = Tabs.Jobs;
  t.refreshTrainButton(onOff);
  Data.options.autoTrain.enabled = onOff;
  if (onOff){
   Data.options.trainTimer = setInterval(function() {t.trainTick(0) }, 3000);
  } 
  else {
   t.dispFeedback(""); // Erase previous feedback
   clearTimeout (Data.options.trainTimer);
  }
 },
 
 setBuildEnable : function (onOff){
  var t = Tabs.Jobs;
  t.refreshBuildButton(onOff);
  Data.options.autoBuild.enabled = onOff;
  if (onOff){
   t.buildRetryTime = 20000;
   Data.options.buildTimer = setInterval (t.buildTick, 4000);
  } 
  else {
   clearTimeout (Data.options.buildTimer);
   Data.options.tJobs.length = 0;
  }
 },

 setResearchEnable : function (onOff){
  var t = Tabs.Jobs;
  t.refreshResearchButton(onOff);
  Data.options.autoResearch.enabled = onOff;
  if (onOff){
   t.resRetryTime = 20000;
   Data.options.researchTimer = setInterval(t.researchTick, 5000);
  } 
  else {
   clearTimeout (Data.options.researchTimer);
   //Data.options.rJobs.length = 0;
  }
 },
 
 refreshTrainButton : function (onOff) {
  var t = Tabs.Jobs;
  var but = $id(UID['tabJobTrain_OnOff']);
  if(!but) return;
  if (onOff){
   but.value = translate('Training').toUpperCase();
   but.className = UID['btn_on'];
  } 
  else {
   but.value = translate('Disabled').toUpperCase();
   but.className = UID['btn_off'];
  }
 },

 refreshBuildButton : function (onOff) {
  var t = Tabs.Jobs;
  var but = $id(UID['tabJobBuild_OnOff']);
  if(!but) return;
  if (onOff){
   but.value = translate('Building').toUpperCase();
   but.className = UID['btn_on'];
  } 
  else {
   but.value = translate('Disabled').toUpperCase();
   but.className = UID['btn_off'];
  }
 },

 refreshResearchButton : function (onOff) {
  var t = Tabs.Jobs;
  var but = $id(UID['tabJobResearch_OnOff']);
  if(!but) return;
  if (onOff){
   but.value = translate('Researching').toUpperCase();
   but.className = UID['btn_on'];
  } 
  else {
   but.value = translate('Disabled').toUpperCase();
   but.className = UID['btn_off'];
  }
 },

 trainStatTick : function (){
  var t = Tabs.Jobs;
  var statElement = $id(UID['tabJobTrain_Report']);
  if (statElement != null){
   statElement.innerHTML = trainTable('train');
  }
 },
 
 // Build statistics - timer set to fire every 1 seconds
 // Calls getBuildJob(), deleteBuildJob(), Buildings.getById(), Seed.fetchPlayer(), serverTime()
 buildStatFetch : false,
 buildStatTick : function (){
  var t = Tabs.Jobs;
  var html = '<TABLE class=' + UID['table'] + '>';
  var len = Data.options.tJobs.length;
  
  for (var cityIdx=0; cityIdx < Seed.cities.length; cityIdx++){
   var city = Seed.cities[cityIdx];
   var job = getBuildJob (cityIdx);
   if (Data.options.tJobs.length == 0 && job) {
    // the Seed is out of sync, the job should be deleted
    deleteBuildJob (cityIdx, job);
    job = null;
   }
   html += '<TR><TD>'+ translate('City') + '&nbsp;' + (cityIdx + 1) +'º</td><TD>';
   
   if (job == null){
    html += translate('Currently Inactive') +'</td></tr>';
   }
   else {
    var b = Buildings.getById(cityIdx, job.city_building_id);
    var timeRemaining = ((job.run_at - serverTime()) > 0) ? timeFormat(job.run_at - serverTime()) : 0;
    if (timeRemaining == 0) {
     // If we have a job and the timeRemaining is negative or zero we delete the job
     // and fetch the Seed - although this does not always work because the server
     // is laggy and may not return the correct information
     html += translate('Awaiting task completion notification') + '...</td><td></td><td></td></tr>';
     deleteBuildJob (cityIdx, job);
     if (t.statFetch == false) {
      Seed.fetchPlayer();
      t.buildStatFetch = true;
     }
    }
    else {
     html += '<b>' + translate('Building') +':</b> </td><td> '+ translate(b.type) + '  ('+ job.level +') </td><td> <font color='+TIMER_COLOR+'>'+ timeRemaining  +'</font></td></tr>';
     t.buildStatFetch = false;
     try{
      $id(UID['tabJobBuild_FB_'+cityIdx+'_'+b.type]).innerHTML = '<font color=#000>' + translate('Building') + ': ' + translate(b.type) + ' ' + translate('Level').toLowerCase() + ' ' + job.level + '</font>';
     }catch(e){}
    }
   }
  }
  $id(UID['tabJobBuild_Report']).innerHTML = html +'</table>';
 },

 // Build statistics - timer set to fire every 1 seconds
 // Calls getResearchJob(), deleteResearchJob(), Seed.fetchPlayer(), serverTime()
 resStatFetch : false,
 researchStatTick : function (){
  var t = Tabs.Jobs, html = '<table class=' + UID['table'] + '>', city = Seed.cities[0];
  var job = getResearchJob (0);
  
  html += '<tr><td>'+ translate('City') +' 1º </td><td>';

  if (job == null){
   html += translate('Currently Inactive') +'</td></tr>';
  }
  else {
   var timeRemaining = ((job.run_at - serverTime()) > 0) ? timeFormat(job.run_at - serverTime()) : 0;
   if (timeRemaining == 0) {
    html += translate('Awaiting task completion notification') + '...</td><td></td><td></td></tr>';
    deleteResearchJob(job);
    if (t.resStatFetch == false) {
     Seed.fetchPlayer();
     t.resStatFetch = true;
    }
   }    
   else {
    // Bug: If we have a job and the timeRemaining is negative or zero we should delete the job
    html += '<b> '+ translate('Researching') +': </b> </td><td> '+ translate(job.research_type) +' ('+ job.level +') </td><td>  <font color='+TIMER_COLOR+'>'+ timeRemaining  +'</font></td></tr>';
    t.resStatFetch = false;
   }
  }

  $id(UID['tabJobResearch_Report']).innerHTML = html +'</table>';
  try{
   $id(UID['tabJobResearch_FB_'+job.research_type]).innerHTML = '<font color=#000>' + translate('Researching') + '&nbsp;' + translate('Level').toLowerCase() + '&nbsp;' + job.level + '</font>';
  }catch(e){}
  //t.statTimer = setTimeout (t.statTick, 5000);
 },

 // Modified to work with jobs
 dispFeedback : function (msg){
  var t = Tabs.Jobs;
  var elementId = '';   

  switch(t.contentType) {
   case 0: break;
   case 1: elementId = 'tabJobTrain_Feedback'; break;
   case 2: elementId = 'tabJobBuild_Feedback'; break;
   case 3: elementId = 'tabJobResearch_Feedback'; break;
  } 
  
  if (elementId && $id(UID[elementId])){
   if (msg == ''){
    $id(UID[elementId]).innerHTML = msg; 
   } else {
    $id(UID[elementId]).innerHTML = new Date().toTimeString().substring (0,8) +'&nbsp;'+  msg;
   }
  }
 },

 // Returns level == 12 if the building is missing
 getCurrentLowestBuildingLevel : function (cityIdx, buildingType){
  var t = Tabs.Jobs, level = 12;
  
  // The building can be missing if it has not been built yet
  try {
   var b = Seed.cities[cityIdx].buildings;
   for (var i=0; i < b.length; i++){
    if (b[i].type == buildingType){
     if (b[i].level < level){
      level = b[i].level;
     }
    }
   }
  }
  catch (e) {
  }  

  return level;
 },

 // Given the city index number and the building type, returns the
 // lowest level building of the specified type or zero if the building
 // is not found (may not have been built)
 // TBD: Check to see if this is needed anymore - we now use getCurrentLowestBuildingLevel() 
 // see above
 getLowestBuildingLevel : function(cityIdx, buildingType){
  var buildings = Seed.cities[cityIdx].buildings;
  var lowest = 12;
  var bFound = false;
  
  for (var p=0; p < buildings.length;p++){
   if (buildings[p].type == buildingType) {
    bFound = true; 
    if (buildings[p].level < lowest){
     lowest = buildings[p].level;
    }
   }
  }
  
  return (bFound) ? lowest : 0;
 },

 // Returns the current research level or zero if the user has not
 // researched this type yet
 // TBD - remove the if statements, make sure that the type passed
 // is UI convolved
 getCurrentResearchLevel : function (research_type){
  var t = Tabs.Jobs, level = 0;
  
  // This can be missing if the user has not done any research
  // implying a research level of zero
  try {
   level = (Seed.player.research[research_type]) ? Seed.player.research[research_type] : 0; 
  }
  catch (e) {
  }  

  return level;
 },
 
 // Return the total number troops of the specified type adding in the qty about to 
 // be produced. If this number is less than the cap, return zero     
 getTroopCap : function(unit_type, qty){
  var t = Tabs.Jobs;
  var cap = 0;
  var completedTroops = 0;
  var marchingTroops = 0;
  
  // Get the cap set for this troop type
  cap = Data.options.troopCap.city[0].units[unit_type];
  
  // If there is no cap, we are done
  if (cap == 0){
   return cap;
  }
  
  // Find the number of troops still in the city  
  completedTroops += (Seed.cities[0].units[unit_type] != undefined) ? Seed.cities[0].units[unit_type] : 0;
  
  // Find additional troops in marches
  for (var id in Seed.marches) {
   for (var q in Seed.marches[id].units){
    if (q == unit_type){
     marchingTroops += Seed.marches[id].units[q];
    }
   }
  }

  // Find troops in training jobs
  /*
  for (var i=0; i< Seed.cities.length; i++)
   var job = getTrainJob(0);
  */
  
  return ((completedTroops + marchingTroops + qty) > cap) ? (completedTroops + marchingTroops + qty) : 0;
 },
 
 // Returns the user set building cap or zero if the cap has not been set
 getBuildingCap : function (cityIdx, buildingType){
  var t = Tabs.Jobs;
  var cap = 0;
  var cityType =  (cityIdx == 0) ? t.capitolCity : t.outpostCity;
  cityType =  (cityIdx == 0) ? cityType.concat(t.capitolField) : cityType.concat(t.outpostField);
  
  for (var i=0; i < cityType.length; i++) {
   if (cityType[i] == buildingType) {
    try {
     cap = (Data.options.autoBuild.buildCap[cityIdx][i]) ? Data.options.autoBuild.buildCap[cityIdx][i] : 0; 
     break;
    }
    catch (e) {
    }  
   }
  }

  return cap;
 },

 // Returns the user set research cap or zero if the cap has not been set
 getResearchCap : function (research_type){
  var t = Tabs.Jobs;
  var cap = 0;
  
  cap = (Data.options.autoResearch.researchCap[research_type]) ? Data.options.autoResearch.researchCap[research_type] : 0; 
  
  return cap;
 },
 
 // Returns the quantity of the specified item type or zero if 
 // the item type is not found
 // Used by the research job
 getItem : function(itemType){
  var items = Seed.player.items;
  var ret = 0;
  for (var p in items) {
   if (p == itemType){
    ret = items[p];
    break;
   }
  }
  return ret;
 },

 // Given the city index number and building type, returns the index
 // of the specified building type
 getBuildingIndex : function (cityIdx, buildingType){
  var t = Tabs.Jobs, bldgIdx = 0;
  var cityType =  (cityIdx == 0) ? t.capitolCity : t.outpostCity;
  cityType =  (cityIdx == 0) ? cityType.concat(t.capitolField) : cityType.concat(t.outpostField);
  
  for (var i=0; i < cityType.length; i++){
   if (cityType[i] == buildingType) { 
    bldgIdx = i;
    break;
   }
  }
  return bldgIdx;
 },
 
 // Used by research jobs
 // This would be simple if only one building of each type existed, but you may build multiple garrisons/training camps
 // So we have to look through the entire list and use an additional parameter to specify the building level needed
 // Returns zero if the specified building is not the at the required level
 getBuildingLevel : function(cityIdx, buildingType, buildingLevel){
  var buildings = Seed.cities[cityIdx].buildings;
  var ret = 0;
  for (var p=0; p < buildings.length;p++) {
   if (buildings[p].type == buildingType && buildings[p].level >= buildingLevel){
    ret = buildings[p].level;
    break;
   }
  }
  return ret;
 },

 // Return the index number of the research type
 getResearchIndex : function (research_type){
  var t = Tabs.Jobs;
  return t.researchIdx[research_type];
 },

 // Training - Get the remainin queue length
 getRemainingQueue : function (city_idx, queueType){
  var city = Seed.cities[city_idx];
  var jobs = city.jobs;
  var maxQueueLength = city.figures.queue_lengths.units;
  var usedQueue = 0;
  // Count the number of jobs in the queue
  for (var i=0; i<jobs.length; i++) {
   if (jobs[i].queue == queueType){
    ++usedQueue;
   }
  }
  return maxQueueLength - usedQueue;
 },

 checkPorterReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Food: 40
  // Garrison Level: 1
  // Idle Population: 1
  // Lumber: 150
  // Metals: 10
  // Upkeep: 2 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 40;
  var garrisonLevel = 1;
  var idlePop = troopQty * 1;
  var lumber = troopQty * 150;
  var metal = troopQty * 10;
  var upkeep = troopQty * 2;
  var city = Seed.cities[0];
  var unit_type = kPorter;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required')+': ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kPorter, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity')+' '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison)+' '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate('TrainingCamp')+' '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food')+': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood')+': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore')+': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble')+': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html += translate('training queue')+' ';
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty + ' ' + translate('Porter') +' '+translate('UpKeep')+' ' + upkeep + ' '+translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkConscriptReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Food: 80
  // Garrison Level: 1
  // Idle Population: 1
  // Lumber: 100
  // Metals: 50
  // Upkeep: 3 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 80;
  var garrisonLevel = 1;
  var idlePop = troopQty * 1;
  var lumber = troopQty * 100;
  var metal = troopQty * 50;
  var upkeep = troopQty * 3;
  var city = Seed.cities[0];
  var unit_type = kConscript;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required')+':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kConscript, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity')+': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food')+': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood')+': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore')+': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('People')+': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html += translate('training queue')+' ';
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kConscript) +' '+translate('UpKeep')+' ' + upkeep + ' '+translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkSpyReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Clairvoyance: 1
  // Food: 120
  // Garrison Level: 2
  // Idle Population: 1
  // Lumber: 200
  // Metals: 150
  // Upkeep: 5 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 120;
  var garrisonLevel = 1;
  var idlePop = troopQty * 1;
  var lumber = troopQty * 200;
  var metal = troopQty * 150;
  var upkeep = troopQty * 5;
  var clairvoyanceLevel = 1;
  var city = Seed.cities[0];
  var unit_type = kSpy;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   clairvoyanceLevel = seedReqs.research[kClairvoyance];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kSpy, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html += translate('Training queue') + ' ';
  if (Seed.player.research.Clairvoyance < clairvoyanceLevel) html += translate(kClairvoyance) + ': ' + clairvoyanceLevel + ' + ';
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate('Spies') +' '+ translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;       
 },

 checkHalberdsmanReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Metallurgy: 1
  // Food: 150
  // Garrison Level: 2
  // Idle Population: 1
  // Lumber: 500
  // Metals: 100
  // Upkeep: 6 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 150;
  var garrisonLevel = 1;
  var idlePop = troopQty * 1;
  var lumber = troopQty * 500;
  var metal = troopQty * 100;
  var upkeep = troopQty * 6;
  var metallurgyLevel = 1;
  var city = Seed.cities[0];
  var unit_type = kHalberdsman;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   metallurgyLevel = seedReqs.research[kMetallurgy];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kHalberdsman, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Metallurgy < metallurgyLevel) html += translate(kMetallurgy) + ': ' + metallurgyLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kHalberdsman) +' '+ translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;       
 },

 checkMinotaurReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Metallurgy: 1
  // Metalsmith: 1
  // Food: 200
  // Garrison Level: 3
  // Idle Population: 1
  // Lumber: 150
  // Metals: 400
  // Upkeep: 7 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 200;
  var garrisonLevel = 3;
  var idlePop = troopQty * 1;
  var lumber = troopQty * 150;
  var metal = troopQty * 400;
  var upkeep = troopQty * 7;
  var metallurgyLevel = 1;
  var metalsmithLevel = 1;
  var city = Seed.cities[0];
  var unit_type = kMinotaur;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   metallurgyLevel = seedReqs.research[kMetallurgy];
   metalsmithLevel = seedReqs.research[kMetalsmith];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kMinotaur, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Metallurgy < metallurgyLevel) html += translate(kMetallurgy) + ': ' + metallurgyLevel +' + '; 
  if (Seed.player.research.Metalsmith < metalsmithLevel) html += translate(kMetalsmith) + ': ' + metalsmithLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kMinotaur) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;          
 },

 checkLongbowmanReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Ballistics: 1
  // Food: 300
  // Garrison Level: 4
  // Idle Population: 2
  // Lumber: 350
  // Metals: 300
  // Upkeep: 9 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 300;
  var garrisonLevel = 4;
  var idlePop = troopQty * 2;
  var lumber = troopQty * 350;
  var metal = troopQty * 300;
  var upkeep = troopQty * 9;
  var ballisticsLevel = 1;
  var city = Seed.cities[0];
  var unit_type = kLongbowman;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   ballisticsLevel = seedReqs.research[kBallistics];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }
  
  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kLongbowman, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Ballistics < ballisticsLevel) html += translate(kBallistics) + ': ' + ballisticsLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kLongbowman) +' '+ translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkSwiftStrikeDragonReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Dragonry: 2
  // Rapid Deployment: 1
  // Rookery: 1
  // Food: 1000
  // Garrison Level: 5
  // Idle Population: 3
  // Lumber: 600
  // Metals: 500
  // Upkeep: 18 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 1000;
  var garrisonLevel = 5;
  var idlePop = troopQty * 3;
  var lumber = troopQty * 600;
  var metal = troopQty * 500;
  var upkeep = troopQty * 18;
  var dragonryLevel = 2;
  var rapidDeploymentLevel = 1;
  var rookeryLevel = 1;
  var city = Seed.cities[0];
  var unit_type = kSwiftStrikeDragon;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   rookeryLevel = seedReqs.buildings[kRookery];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   rapidDeploymentLevel = seedReqs.research[kRapidDeployment];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kSwiftStrikeDragon, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (t.getBuildingLevel(0, kRookery, rookeryLevel) == 0) html += translate(kRookery) +': '+ rookeryLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Dragonry < dragonryLevel) html += translate(kDragonry) + ': ' + dragonryLevel +' + '; 
  if (Seed.player.research.RapidDeployment < rapidDeploymentLevel) html += translate(kRapidDeployment) + ': ' + rapidDeploymentLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kSwiftStrikeDragon) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret; 
 },

 checkBattleDragonReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Dragonry: 3
  // Rapid Deployment: 5
  // Rookery: 5
  // Food: 1000
  // Garrison Level: 7
  // Idle Population: 6
  // Lumber: 500
  // Metals: 2500
  // Upkeep: 35 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 1000;
  var garrisonLevel = 7;
  var idlePop = troopQty * 6;
  var lumber = troopQty * 500;
  var metal = troopQty * 2500;
  var upkeep = troopQty * 35;
  var dragonryLevel = 3;
  var rapidDeploymentLevel = 5;
  var rookeryLevel = 5;
  var metalsmithLevel = 5;
  var city = Seed.cities[0];
  var unit_type = kBattleDragon;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   rookeryLevel = seedReqs.buildings[kRookery];
   metalsmithLevel = seedReqs.buildings[kMetalsmith];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   rapidDeploymentLevel = seedReqs.research[kRapidDeployment];
   dragonryLevel = seedReqs.research[kDragonry];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kBattleDragon, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (t.getBuildingLevel(0, kMetalsmith, metalsmithLevel) == 0) html += translate(kMetalsmith) +': '+ metalsmithLevel +' + ';
  if (t.getBuildingLevel(0, kRookery, rookeryLevel) == 0) html += translate(kRookery) +': '+ rookeryLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Dragonry < dragonryLevel) html += translate(kDragonry) + ': ' + dragonryLevel +' + '; 
  if (Seed.player.research.RapidDeployment < rapidDeploymentLevel) html += translate(kRapidDeployment) + ': ' + rapidDeploymentLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kBattleDragon) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;     
 },

 checkArmoredTransportReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Factory: 3
  // Levitation: 3
  // Food: 600
  // Garrison Level: 6
  // Idle Population: 4
  // Lumber: 1500
  // Metals: 350
  // Upkeep: 10 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 600;
  var garrisonLevel = 6;
  var idlePop = troopQty * 4;
  var lumber = troopQty * 1500;
  var metal = troopQty * 350;
  var upkeep = troopQty * 10;
  var factoryLevel = 3;
  var levitationLevel = 3;
  var city = Seed.cities[0];
  var unit_type = kArmoredTransport;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   factoryLevel = seedReqs.buildings[kFactory];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   levitationLevel = seedReqs.research[kLevitation];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kArmoredTransport, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (t.getBuildingLevel(0, kFactory, factoryLevel) == 0) html += translate(kFactory) +': '+ factoryLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ ((lumber - city.resources.wood)) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ ((metal - city.resources.ore)) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Levitation < levitationLevel) html += translate(kLevitation) + ': ' + levitationLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kArmoredTransport) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkGiantReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Clairvoyance: 3
  // Factory: 7
  // Metallurgy: 8
  // Metalsmith: 5
  // Food: 4000
  // Garrison Level: 9
  // Idle Population: 8
  // Lumber: 6000
  // Metals: 1500
  // Upkeep: 100 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 4000;
  var garrisonLevel = 8;
  var idlePop = troopQty * 8;
  var lumber = troopQty * 6000;
  var metal = troopQty * 1500;
  var upkeep = troopQty * 100;
  var factoryLevel = 7;
  var metalsmithLevel = 7;
  var clairvoyanceLevel = 3;
  var metallurgyLevel = 8;
  var city = Seed.cities[0];
  var unit_type = kGiant;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   factoryLevel = seedReqs.buildings[kFactory];
   metalsmithLevel = seedReqs.buildings[kMetalsmith];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   clairvoyanceLevel = seedReqs.research[kClairvoyance];
   metallurgyLevel = seedReqs.research[kMetallurgy];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kGiant, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (t.getBuildingLevel(0, kFactory, factoryLevel) == 0) html += translate(kFactory) +': '+ factoryLevel +' + ';
  if (t.getBuildingLevel(0, kMetalsmith, metalsmithLevel) == 0) html += translate(kMetalsmith) +': '+ metalsmithLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Clairvoyance < clairvoyanceLevel) html += translate(kClairvoyance) + ': ' + clairvoyanceLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kGiant) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkFireMirrorReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Ballistics: 10
  // Factory: 9
  // Metallurgy: 10
  // Food: 5000
  // Garrison Level: 10
  // Idle Population: 10
  // Lumber: 5000
  // Metals: 1200
  // Stone: 8000
  // Upkeep: 250 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 5000;
  var garrisonLevel = 10;
  var idlePop = troopQty * 10;
  var lumber = troopQty * 5000;
  var metal = troopQty * 1200;
  var stone = troopQty * 8000;
  var upkeep = troopQty * 250;
  var factoryLevel = 9;
  var metallurgyLevel = 10;
  var ballisticsLevel = 10;
  var city = Seed.cities[0];
  var unit_type = kFireMirror;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   factoryLevel = seedReqs.buildings[kFactory];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   stone = troopQty * seedReqs.resources['stone'];
   ballisticsLevel = seedReqs.research[kBallistics];
   metallurgyLevel = seedReqs.research[kMetallurgy];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kFireMirror, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (city_idx == 0){ 
   if (t.getBuildingLevel(city_idx, kGarrison, garrisonLevel) == 0) html += translate(kGarrison) +': '+ garrisonLevel +' + ';
  }
  else if (t.getBuildingLevel(city_idx, kTrainingCamp, garrisonLevel) == 0) html += translate(kTrainingCamp) +': '+ garrisonLevel +' + ';
  if (t.getBuildingLevel(0, kFactory, factoryLevel) == 0) html += translate(kFactory) +': '+ factoryLevel +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  if (city.resources.stone < stone) html += translate('Stone') +': '+ (stone - city.resources.stone) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(city_idx, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Metallurgy < metallurgyLevel) html += translate(kMetallurgy) + ': ' + metallurgyLevel +' + '; 
  if (Seed.player.research.Ballistics < ballisticsLevel) html += translate(kBallistics) + ': ' + ballisticsLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kFireMirror) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkAquaTroopReqs : function(troopQty, city_idx, count, troopsLength) {
  // Requirements
  // Clairvoyance: 4
  // Rapid Deployment: 8
  // Factory: 7
  // Metallurgy: 10
  // Food: 4000
  // TrainingCampe Level: 10
  // Idle Population: 10
  // Lumber: 5500
  // Metals: 2500
  // Stone: 7000
  // Upkeep: 125 food
  
  var t = Tabs.Jobs;    
  var food = troopQty * 5000;
  var trainingCampLevel = 10;
  var idlePop = troopQty * 10;
  var lumber = troopQty * 5000;
  var metal = troopQty * 1200;
  var stone = troopQty * 8000;
  var upkeep = troopQty * 250;
  var factoryLevel = 7;
  var metalsmithLevel = 7;
  var rapidDeploymentLevel = 8;
  var clairvoyanceLevel = 4;
  var respiratorQty = troopQty;
  var city = Seed.cities[0];
  var unit_type = kAquaTroop;
  
  try {
   var seedReqs = Seed.requirements.troop[unit_type];
   food = troopQty * seedReqs.resources['food'];
   garrisonLevel = seedReqs.buildings[kGarrison];
   factoryLevel = seedReqs.buildings[kFactory];
   metalsmithLevel = seedReqs.buildings[kMetalsmith];
   idlePop = troopQty * seedReqs.population['idle'];
   lumber = troopQty * seedReqs.resources['wood'];
   metal = troopQty * seedReqs.resources['ore'];
   stone = troopQty * seedReqs.resources['stone'];
   rapidDeploymentLevel = seedReqs.research[kRapidDeployment];
   clairvoyanceLevel = seedReqs.research[kClairvoyance];
  }
  catch(e){
   verboseLog('Training: ' + e.msg + ' Manifest not available, using defaults');
  }

  var html = '';
  var n = translate('Required') + ':+ ';
  var ret = {trainable:false, msg:[]};
  var troopCapped = t.getTroopCap(kAquaTroop, troopQty);
  
  // If the troop is capped, are we about to exceed the limit?
  if (troopCapped > 0) html += translate('Capacity') +': '+ troopCapped +' + ';
  
  // Returns zero or the building level
  if (t.getBuildingLevel(city_idx, kTrainingCamp, trainingCampLevel) == 0) html += translate(kTrainingCamp) +': '+ trainingCampLevel +' + ';
  if (t.getBuildingLevel(0, kFactory, factoryLevel) == 0) html += translate(kFactory) +': '+ factoryLevel +' + ';
  if (t.getBuildingLevel(0, kMetalsmith, metalsmithLevel) == 0) html += translate(kMetalsmith) +': '+ metalsmithLevel +' + ';
  var availableRespirators = t.getItem(kAquaTroopRespirator);
  if (availableRespirators < respiratorQty) html += translate('Respirators') +': '+ (respiratorQty - availableRespirators) +' + ';
  if (city.resources.food < food) html += translate('Food') +': '+ (food - city.resources.food) +' + ';
  if (city.resources.wood < lumber) html += translate('Wood') +': '+ (lumber - city.resources.wood) +' + ';
  if (city.resources.ore < metal) html += translate('Ore') +': '+ (metal - city.resources.ore) +' + ';
  if (city.resources.stone < stone) html += translate('Stone') +': '+ (stone - city.resources.stone) +' + ';
  var availablePop = city.figures.population.current - city.figures.population.laborers - city.figures.population.armed_forces;
  availablePop = (availablePop > 0) ? availablePop : 0;
  if (availablePop < idlePop) html += translate('Peoble') + ': ' + (idlePop - availablePop) + ' + ';
  if (t.getRemainingQueue(1, 'units') == 0) html+= translate('Training queue') + ' ';
  if (Seed.player.research.Clairvoyance < clairvoyanceLevel) html += translate(kClairvoyance) + ': ' + clairvoyanceLevel +' + '; 
  if (Seed.player.research.RapidDeployment < rapidDeploymentLevel) html += translate(kRapidDeployment) + ': ' + rapidDeploymentLevel +' + '; 
  if (html.length == 0) {
   ret.trainable = true;
   ret.msg = troopQty +' '+ translate(kAquaTroop) +' ' + translate('UpKeep') + ' ' + upkeep + ' ' + translate('Food');
  }
  else {
   ret.trainable = false;
   ret.msg = n + html;
  }
  return ret;    
 },

 checkStone

0 comments:

Post a Comment