
//IE style javascript check, needed for special handling of mouse events.
var IE = document.all?true:false;

//setup variables to get the city designer to work.

var mouseX = 1000;
var mouseY = 1000; //real position of cursor.- initial value intended to simulate out-of-city.


//initialize the element in which the city is drawn properly.
var building_data = getBuildingData(); //read from building_data.js's array returning method.
var buildings; 							//list of all buildings in the city. unfortunately includes shadow.
var selectedBuilding = 1; 				//currently selected building: personal residence.

var city= gE("citydiv");
var cityX= findPos(city)[0];
var cityY= findPos(city)[1];
var cityX1= cityX + city.offsetWidth;
var cityY1= cityY + city.offsetHeight;
city.style.backgroundImage = "url( "+backgroundImageURL+" )";  //assign background

var focusOutCity = true;// default to true, so we may immediately enter code.

var rotation = 270;
var shadow = newShadow(selectedBuilding,true); //this is the prospected building. trailing the mouse, start with personal residence.
var coordTag = createCoordTag();

var error; //can contain the error for parse errors.


var optionsDiv = gE("optdiv");
var helpDiv = gE("helpdiv");
var errDiv = gE("errdiv");
var mode = "placing";
var prevImg = gE('previmg');

var selTab = gE("firsttab"); //tab to which the selected building belongs.
var curTab= gE("firsttab"); //currently selected tab.
var lastTab = gE("firsttab"); // last (previous) facility size selector tab.
var homeTab = gE("firstTab");
var lastFullTab = gE("firsttab"); //the last 'fully' selected tab (after delay.)
var tabDelay; //reference to timeout object.
var homeTabDelay; //reference to timeout object that returns the tab home.

var CSSElement = document.createElement("div"); //todo remove this?

var homeTabColor = "#011A8B";//"#2222FF"; //"#004570";
var tabColor = "#00243D"; //color of the tabs.
var preTabColor =  "#002F40"; //color of the tabs.
var tabContainer = gE("tabContainer");
var notAllowedReason; //string that contains the reason the last building was not allowed.

//prevent immediate calculations
var statdelaytimes =0;
var statdelaytimer;


//setTimeout("trace(frData);",1000);
var textField = gE("textfield");
var facList = gE("faclist");

//assigning these variables once, rather than locally within function calls will yield a significant speed increase.
var powTag = gE("pow");
var jobTag = gE("job");
var flatTag = gE("flat");
var erTag = gE("er");
var melTag = gE("mel");
var durTag = gE("dur");
var rudTag = gE("rud");
var lomTag = gE("lom");
var varTag = gE("var");
var tibTag = gE("tib");
var quaTag = gE("qua");
var bacTag = gE("bac");
var massTag = gE("matmass");
var volTag = gE("matvol");

var facPowTag = gE("facpow");
var facJobTag = gE("facjob");
var facFlatTag = gE("facflat");
var facMelTag = gE("facmel");
var facDurTag = gE("facdur");
var facRudTag = gE("facrud");
var facLomTag = gE("faclom");
var facVarTag = gE("facvar");
var facTibTag = gE("factib");
var facQuaTag = gE("facqua");
var facBacTag = gE("facbac");

var powTot = 0;
var jobTot = 0;
var flatTot	= 0;
var melTot = 0;
var durTot = 0;
var rudTot = 0;
var lomTot = 0;
var varTot = 0;
var tibTot = 0;
var quaTot = 0;
var bacTot = 0;
var masTot = 0;
var volTot = 0;

//needs to be before initialisation stuff regarding freighters.
var frData = getFreighterData();
var haulTag = 		gE("haultag");
var capTag = 		gE("capacity");
var freighterList =	gE("freighterlist");


//initialize stuff needed for calculating freighter hauls.


var freighter = new Object; //stores current freighter data.
freighter.name = '';
freighter.volCap = 0;
freighter.weightCap = 0;


var keyVal = -1; //last performed action. prevents double-action execution using keyboard shortcuts, boosting speed.
var lastX;
var lastY;
var keyLastX;
var keyLastY;
var check2Sides = gE("check2").checked;
var hidden = gE("hiddencheck").checked;



//list of the names of the terrains.
/*var terrainTypes = [
        'error!',    //first of the values. inserted so that 1=cave, as in the selector.
        'cave',
        'crater',
        'desert',
        'forest',
        'gasgiant',
        'glacier',
        'grassland',
        'jungle',
        'mountain',
        'ocean',
        'river',
        'rock',
        'swamp'];*/

var terrainType = gE("terrainlist").value = 5;
var terrainName = terrainTypes[terrainType];


//end of variable initialisation, now things get set that determine behaviour, via methods.
setTerrain(terrainType); 					//get terrainName etc to proper values.
fillFreighterList();
showFacilities(1,1,gE("firstTab"),true); //imediately show the first tab.
selectHomeTab(); 							//show the tab that is selected (fix colour to the selected tab colour.)

city.onmousemove = getMouseXY;
city.onmouseover = function(){ focusOutCity = false;};
city.onmousedown = newBuilding;
city.onmouseout = function() { mouseX = 1000; mouseY = 1000;  focusOutCity = true; hideShadow(); };
gE("faclist").onmousemove = function(){ clearTimeout(homeTabDelay); homeTabDelay = setTimeout(function(){selectHomeTab();},4000); };
document.onkeydown = handleKeys;
updatePreview();
getCodeFromURL(); //last thing to do when initialising.


function hideShadow()
{

setTimeout( function()
{
    if (focusOutCity)
    {
    coordTag.style.left = shadow.style.left = mouseX+"px";
    coordTag.style.top = shadow.style.top =mouseY+"px";
    }
}
,100);


}

 //fix to prevent shadow from being seen as a real building during initial validation.
function fixShadow()
{
    shadow.x = 1000;
    shadow.y = 1000;
    shadow.y1 = 1001;
    shadow .x1 = 1001;
    coordTag.style.left = shadow.style.left = mouseX+"px";
    coordTag.style.top = shadow.style.top =mouseY+"px";
    }

//abbreviation!
function gE(str)
{
  return document.getElementById(str);
}

//cause it'll get all destroyed and such. (clear map.)
function createCoordTag()
{
    coordTag = document.createElement("div");
    coordTag.className = "coordTag"; //apply coord tag styles.
    coordTag.style.opacity = "0.5"; //taken from CSS so validation monkeys such as greenberg will not complain.
    if(IE) coordTag.style.filter = "alpha(opacity=0.5)"; 	//little do they know though. ignorance is bliss.
    return coordTag;
}

function updateCoordTag()
{
    city.appendChild(coordTag);
    coordTag.style.left = shadow.x-23 + "px";
    coordTag.style.top = shadow.y-20 + "px";
    coordTag.innerHTML = ""+ (shadow.x/25+1)+","+(shadow.y/25+1)
}

//createBuilding(19,25,25,90,true); //test to see if it worked. it did.
//parseBuilding("2,1,1,v"); //same thing as above, the id however is the external(swc) one.


//good function to find the real position of an alement within Mozilla and Opera (and IE), not my work.
//URL: http://www.quirksmode.org/js/findpos.html
function findPos(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        curleft = obj.offsetLeft
        curtop = obj.offsetTop
        while (obj = obj.offsetParent) {
            curleft += obj.offsetLeft
            curtop += obj.offsetTop
        }
    }
    return [curleft,curtop];
}


//process keyboard events that occur while the cursor is within the city confines.
//quirky in IE, because it requires the element to be.. 'selected' - by clicking on it; clicking again de-selects?!
function handleKeys(e)
{
    //don't handle events occuring while outside of the city limits.
    if(focusOutCity) return;

    textField.blur();

    if (!e) e = window.event;
    var key = String.fromCharCode(e.keyCode).toUpperCase();

    //dont execute if situation has not changed.
    if((keyVal != e.keyCode) || ((shadow.x != keyLastX) || (shadow.y != keyLastY)) )
    {
        if(key == "D")  remove();
        else if(key == "F")  fetch();
        else if(key == "N")  switchmode();
        else if(key == "S")  writeText();
        else if(key == "C")  placeBuilding();

        //update key-situation status.
        keyVal = e.keyCode;
        keyLastX = shadow.x;
        keyLastY = shadow.y;
    }

    if(key == "R")  rotate(); //this function is exempted from the rule above.
}



//function to fill the list of freighters.
function fillFreighterList()
{
    freighterList.innerHTML = ""; //clear the one required option tag for it to be valid xhtml
    freighterList.onchange = function() { changeFreighter(); }
    frData = getFreighterData();


    var length = frData.length/3;
    for(var i = length; i > 0;i--)
    {
        newOption = document.createElement("option");
        newOption.text =  frData[i*3-3];
        newOption.value = newOption.text; //for IE.

        if(!IE) newOption.title = "Mass: "+ frData[i*3-1] + " , Volume: " + frData[i*3-2]; //IE sucks

        if(!IE)freighterList.add(newOption,null);
        else freighterList.add(newOption);
    }

    if(!IE)freighterList.value = "Bulk Freighter"; //default option. //IE sucks
    else freighterList.value = "Bulk Freighter";
    changeFreighter(); //refresh now.
}


//remove the building that the mouse is hovering over.
function remove()
{
    try
    {
        deleteBuilding(findBuildingByCoords(mouseX-12,mouseY-12)); //compensate for offset.
        shadowCollisionCheck(true);
    }
    catch(e) { ; };
}


//'grabs' the building hovered over, by removing that building, and making it the prospected building.
function fetch()
{
    try
    {
        var building = findBuildingByCoords(mouseX-12,mouseY-12);
        rotation = building.rotation;
        if(building.facData.id0+1) //+1 due to array structure/access blah.
        {
            changeShadow(building.facData.id0+1);
            deleteBuilding(building);
        }
    }
    catch(e) {;}
}


//useful for the keyboard shortcuts.
//finds a childnode of the city div tag, which has been properly set up as a building (various extra attributess.)
function findBuildingByCoords(px,py)
{
    var nodes = city.childNodes;
    for(var i = 0; i < nodes.length;i++)
    {
        var node = nodes[i]
        if ( (node.buildingId)  && (node != shadow))
        {
            if ((node.x < px) && (node.x1 > px))
            {
                if ((node.y < py) && (node.y1 > py))
                {
                    return node;
                }
            }
        }
    }
    return null; //not found.
}

//attempt to fix shadow so initial validate wont fail.

//check wether all the placed buildings are allowed. if Not, add to HTML list with specs
//if del, generate alternate report, state what was deleted.
//return bool returns wether valid or not.
function ValidateBuildings(del)
{
    fixShadow();
    var text = "";
    var numFlaws =0;
    var nodes = city.childNodes;
    for(var i = nodes.length-1; i >= 0;i--)
    {
       node = nodes[i];
       //must be a building, and must not be the unplaced/prospected building.
        if((node.buildingId) && !(node == shadow))
        {
        //crap: || !(buildingFits(node) ==1)
            if(!isAllowed(node.facData.id,node.w/25,node.h/25)  || !(buildingFits(node,true) == 1) || (!buildingFitsProperly(node,false,true))   )
            {
                if(del) city.removeChild(node); //delete.
                numFlaws++;
                text += "<li>"+node.facData.name + " at ["+(node.x/25+1)+","+(node.y/25+1)+"]";
                text += "<br/>Reason: <i style='color:#8888FF''>"+notAllowedReason+"</i></li>";
            }
        }
    }
    if(numFlaws > 0) //if actually anything wrong.
    {
        if(!del) errDiv.innerHTML = "<p align='center' style='font-weight:bold' >There are "+numFlaws+" illegally placed Buildings in this design</p>";
        else
        {
            calculateMaterials();
            errDiv.innerHTML = "<p align='center' style='font-weight:bolder'>"+numFlaws+" illegally placed facilities have been Deleted. Below is the list.</p>";
        }

        errDiv.innerHTML += text;
        errDiv.innerHTML += "<p align='center'> Click anywhere inside this screen to close</p>";
        if(!del) errDiv.innerHTML += "<p align='center'>correct the problems manually Or <input type=button value='*Delete These Buildings' onclick='ValidateBuildings(true);' > <br/>(*note: auto-delete may not be very efficient!) </p>"
        errDiv.style.display = 'block';
        errDiv.onClick = function(){ this.style.display='none'};
        return false; //not valid
    }
    return true; //valid.
}



//[wARNING this function can be outdated by SWC changes in facility IDs!]
//is the provided SWC facility ID allowed to be on the city?
function isAllowed(buildingId,w,h,orientation)
{
   //mountains, glaciers, craters, if bigger than 5*5 disallow.
   //do not approve just yet. possibly hidden wants a go.
    if ( (terrainType == 11) || (terrainType == 10) || (terrainType == 8) )
    {
     if( (w > 5) || (h > 5) ) { notAllowedReason = "Too large to fit in "+terrainName+".";  return false; }
    }

    //cave, if OK here, approve completely. hidden-check. is more lax.
    else if (terrainType == 13)
    {
        var tempW = Math.max(w,h);
        w = h;
        //h = tempW;
        if(w > 3 && h > 5)
        {
            if(parseInt(buildingId) == 36) return true; //mine
            notAllowedReason = "too large to fit in "+terrainName;
            return false;
        }
        else
        {
            buildingId = parseInt(buildingId);
            //no kdy,lnr1,lnr2,shieldgen.
            if ((buildingId >= 25) && (buildingId <= 28)) {
                notAllowedReason = "This facility is not allowed in "+terrainName+"s.";  return false;
            }

            //no drydock, port or naval base. (72 is unused)
            if ((buildingId >= 71) && (buildingId <= 73)) {
                notAllowedReason = "This facility is not allowed in "+terrainName+"s.";  return false;
            }

            return true; //skip hidden.
       }
    }
    //if hidden, check it's OK, otherwise approve now.
    //must be max 3*5 (except for: factory, hospital, mine, training academy, alazhi farm, Command Centre)
    if(hidden)
    {
       //fix for 3*5 check.
        var tempW = Math.max(w,h);
        w = h;
        //h = tempW;

        if(w > 3 && h > 5)
        {
            buildingId = parseInt(buildingId);
            //mine
            if(buildingId == 36) return true;
            if(buildingId == 34) return true; //factory
            if(buildingId == 33) return true; //hospital
            if(buildingId == 32) return true; //training academy
            if(buildingId == 59) return true; //command center
            if(buildingId == 65) return true; //alazhi farm
            if(buildingId == 86) return true; //1x10 wall
            if(buildingId == 87) return true; //1x19 wall
            notAllowedReason = "Not allowed in hidden cities."
            return false;
        }
        return true;
    }

 return true;
}


//change the background for the city.
function setTerrain(intVal){
    terrainName = terrainTypes[intVal]; //used for buildings allowed.
    gE("terrainlist").value = intVal;
    gE('terraindiv').style.backgroundImage = 'url('+terrainURL+resolveTerrainImage(terrainName)+'/terrain.gif)';
    terrainType = intVal;
    ValidateBuildings();
    resetFacilities();
}

function rotate(){
   if(rotation == 270){
       rotation = 0;
   } else {
       rotation = 270;
   }
   shadow = newShadow(selectedBuilding,true); //update shadow. also add.
   updateCoordTag();
   if(mode == 'placing') {
       city.appendChild(shadow); city.insertBefore(coordTag,shadow)
   }
   updatePreview();
}

//toggle options screen.: actually its now an about screen.
function toggleOptions(){
     if(optionsDiv.style.display == 'block'){
         optionsDiv.style.display = 'none';
     } else {
         optionsDiv.style.display = 'block';
     }
}

//reset the facility list, only if necessary.
function resetFacilities()
{
    ValidateBuildings(); //check all buildings are allowed.
    facData = getFac(selectedBuilding);

    //if current building is not allowed, switch to personal residence.
    if(!isAllowed(shadow.buildingId, facData.width / 25, facData.height / 25)){
        showFacilities(1, 1, gE("firstTab"));
        selectedBuilding = 1;
        shadow = newShadow(selectedBuilding, true);
        updatePreview();
    }

}


//debug, list all facility stats. Keep this. always useful as facility info may change.
for(var i = 1000; i < 80 ;i++){
a = getFac(i);

alert(
     "\n id0 "+a.id0 +
    "\n width "+a.width +
    "\n height "+a.height +
    "\n id "+a.id +
    "\n name "+a.name +
    "\n pow "+a.pow +
    "\n job "+a.job +
    "\n flat "+a.flat +
    "\n mel "+a.mel +
    "\n dur "+a.dur +
    "\n rud "+a.rud +
    "\n lom "+a.lom +
    "\n var "+a.vari +
    "\n tib "+a.tib +
    "\n qua "+a.qua +
    "\n bac "+a.bac);
}


//function to retrieve Facility information with.
function getFac(facid)
{
    var fac = new Object();
    //  0,1,2,3			,4   ,5  ,6  ,7   ,8  ,9  ,10 ,11 ,12 ,13 ,14 ,15
   //,id,w,h,BuildingId,name,pow,Job,flat,Mel,dur,rud,lom,Var,Tib,Qua
    fac.id0 = 		building_data[(facid*18-18+0)]; //internal ID, used in most functions dealing with buildings, int.
    fac.width = 	building_data[(facid*18-18+1)]*25;
    fac.height = 	building_data[(facid*18-18+2)]*25;
    fac.id = 		building_data[(facid*18-18+3)]; //real/SWC identifier, is a string.
    fac.name = 		building_data[(facid*18-18+4)];
    fac.pow = 		building_data[(facid*18-18+5)];
    fac.job = 		building_data[(facid*18-18+6)];
    fac.flat = 		building_data[(facid*18-18+7)];
    fac.mel = 		building_data[(facid*18-18+8)];
    fac.dur = 		building_data[(facid*18-18+9)];
    fac.rud = 		building_data[(facid*18-18+10)];
    fac.lom = 		building_data[(facid*18-18+11)];
    fac.vari = 		building_data[(facid*18-18+12)]; //used to be named var. Works, but not under IE.
    fac.tib = 		building_data[(facid*18-18+14)];
    fac.qua = 		building_data[(facid*18-18+15)];
    fac.bac = 		building_data[(facid*18-18+17)];

    return fac;
}


//building id, x coord, y coord, rotation: v,h or 0 1 2 3 (starting from top up, going clockwise)
//updatemat is true when you want to immediately calculate materials.
// function's content mostly copied from newShadow() method.
function createBuilding(id,x,y,rot,updateMat)
{
    rot = rot%360;
    //create and add the new building to the city.

    newBuilding = document.createElement("div");

    newBuilding.x = x;
    newBuilding.y = y;
    newBuilding.buildingId = id;
    newBuilding.rotation = rot;

    //acquire information from the array describing the buildings.
    var facData = getFac(newBuilding.buildingId);
    newBuilding.buildingId = facData.id;
    newBuilding.facData = facData;
    newBuilding.style.left = x + "px";
    newBuilding.style.top = y + "px";

    if((rot % 180) == 0){
        newBuilding.w = facData.width;
        newBuilding.h = facData.height;
    } else {
        newBuilding.h = facData.width;
        newBuilding.w = facData.height;
    }

    newBuilding.innerHTML = "<img src='"+getImageURL(newBuilding.buildingId,newBuilding.rotation)+"' width='"+newBuilding.w+"' height='"+newBuilding.h+"' >"

    newBuilding.style.width= newBuilding.w + "px";
    newBuilding.style.height= newBuilding.h + "px";
    newBuilding.facData = facData; //easier and faster than adding all individual materials.
    newBuilding.x1 = newBuilding.x +newBuilding.w;
    newBuilding.y1 = newBuilding.y +newBuilding.h;

    //normal visual representation.
    newBuilding.style.backgroundColor= "#003366";

    newBuilding.style.color= "#FF0000";
    newBuilding.style.position= "absolute";
    newBuilding.style.opacity= "1";

    //IE version of opacity.
    if(IE){
        newBuilding.style.filter = "alpha(opacity=100)";
    }

    //adding material.
    if(updateMat){
        updateMaterials(newBuilding.facData,true);
    }
    city.appendChild(newBuilding);
    return newBuilding; //return nice reference.
}



//properly deletes an individual building.
//(for removing all buildings, a mass-discard simply takes place, and materials are recalced.)
function deleteBuilding(tag) {
    if((tag.facData) && (tag != shadow)) {
        try{
            tag.parentNode.removeChild(tag);
        } catch(e) {
            ;
        }

        updateMaterials(tag.facData, false);
        buildings = document.getElementsByName("building");
    }
}


// Id (not building Id), buiding name, gives a link that changes prospected building type when clicked.
function givelink(id,name) {
  return "+ <a class='faclink' onclick='changeShadow("+id+")'>"+name+"</a>";
}

//updates the preview image, and corresponding facility information.
function updatePreview() {
    //75 = max diameter
    if((shadow.h > 75) || (shadow.w > 75)){
        var scale = 75/Math.max(shadow.h,shadow.w);
    } else {
        var scale = 1;
    }

    var a = getFac( findInternalBuildingId(shadow.buildingId)+1);

    facPowTag.innerHTML = a.pow;
    facJobTag.innerHTML = a.job;
    facFlatTag.innerHTML= a.flat;
    facMelTag.innerHTML = a.mel;
    facDurTag.innerHTML = a.dur;
    facRudTag.innerHTML = a.rud;
    facLomTag.innerHTML = a.lom;
    facVarTag.innerHTML = a.vari;
    facTibTag.innerHTML = a.tib;
    facQuaTag.innerHTML = a.qua;
    facBacTag.innerHTML = a.bac;

    gE("facname").innerHTML = "<a style='font-weight:bold;' TARGET='_blank' href='http://www.swcombine.com/rules/technology/facilities/details.php?id="+a.id+"' >"+a.name+"</a>";
    gE("facdata").innerHTML = " Rotated "+(rotation+"").replace(270,"vertically").replace(0,"horizontally")+" giving: ["+(shadow.w/25)+"x"+(shadow.h/25)+"] (id:"+a.id+")";

    prevImg.innerHTML = "<img id='previmg' height='"+Math.floor(shadow.h*scale)+"' width='"+Math.floor(shadow.w*scale)+"' src='"+getImageURL(a.id,rotation)+"'  alt='"+a.name+"' title='"+a.name+"' >";
}


//make a new shadow and update pertaining global vars.
function changeShadow(id){
    selTab.style.backgroundColor = ''; //reset colour of old tab.
    selTab = curTab; //remember to which tab building belongs.

    shadow = newShadow(id,true);
    updateCoordTag();
    selectedBuilding=id;
    showFacilities(shadow.facData.width/25,shadow.facData.height/25,curTab,true); //instant refresh
    updatePreview();


    homeTab.style.backgroundColor = '';
    homeTab = selTab;

    lastTab = selTab;

}


//immediately switch to the tab containing the selected building.
//this is useful for avoiding confusion as to which building is selected.
function selectHomeTab(){
    var w = shadow.facData.width/25;
    var h = shadow.facData.height/25;

    var tabs = tabContainer.childNodes;


    if(selTab) selTab.style.backgroundColor = curTab.style.backgroundColor= '';
    homeTab.style.backgroundColor = homeTabColor;

    for(var i = 0; i < tabs.length ;i++)
    {
        //trace( tabs[i].innerHTML+ " ?== "+w+"x"+h+"<br/>");
        if (( tabs[i].innerHTML == (w+"x"+h)) || (tabs[i].innerHTML == (h+"x"+w)) )
        {
            selTab = tabs[i];
            realShowFacilities(w,h,tabs[i],true,true);
            return;
        }
    }
    alert("something terrible is wrong and I cannot select the correct tab.");
        alert(tabContainer);
}


//prevents the facility list from going to show new ones. (example: when mouse out of )
function dontShowFacs()
{
    clearTimeout(tabDelay); //cancel full selection.
    clearTimeout(homeTabDelay); //fout?
    //selTab.style.backgroundColor = '';
    if(curTab)  curTab.style.backgroundColor='';
    if((curTab) && (curTab != selTab)) curTab.style.backgroundColor=''; //clear special color.

    //NOTE must use the x in facility tab.
    var size = lastFullTab.innerHTML.split("x");
    showFacilities(size[1],size[0],lastFullTab,true); //note: x and y inversed :/

}

//list the facilities in the faclist div tag.
//if you dont want delay,specify true as fourth param

function showFacilities(x,y,tab,noDelay)
{
    //while browsing, dont set delay, but if you stop browsing, then after delay switch to hometab.
    clearTimeout(homeTabDelay);
    tab.onmouseout =  function() { clearTimeout(homeTabDelay); homeTabDelay = setTimeout(function(){selectHomeTab();},4000); };

    if (tab) curTab =tab;
    if ( (lastTab) && (lastTab != selTab) ) lastTab.style.backgroundColor = '';
    tab.style.backgroundColor = preTabColor;
    homeTab.style.backgroundColor = homeTabColor;
    lastTab = tab;

    clearTimeout(tabDelay);
    tabDelay = setTimeout(function(){ realShowFacilities(x,y,tab); } ,(noDelay)?0:100);
}

//do not call this function directly, unless from selectHomeTab.
//if you dont want the tab to try to return home, specify true for fifth param.
function realShowFacilities(x,y,tab,noHomeDelay)
{

    tab.style.backgroundColor = tabColor;
       homeTab.style.backgroundColor = homeTabColor;

    var data = "";
    var facList = gE("faclist");
    var maxrows = 8; //limit height.
    var rows = 0;

    //var faclist = new Array();
    //  0,1,2,3			,4   ,5  ,6  ,7   ,8  ,9  ,10 ,11 ,12 ,13 ,14 ,15, 16 ,17
   //,id,w,h,BuildingId,name,pow,Job,flat,Mel,dur,rud,lom,Var,Tib,Qua,Bac
   data = "<table class='faclist' width=100% height='145' <tr style='background-color:"+tabColor+"'><td class='facrow' style='background-color:"+tabColor+"'>";
   //console.dir(building_data);
    for(var i =1;i < building_data.length;i++)
    {
      if( x == building_data[(i*18-18+1)] )
      if( y == building_data[(i*18-18+2)] )
      {
          if(++rows >= maxrows) { data += "</td><td class='facrow' style='background-color:"+tabColor+"'>"; rows=0; }

            if(building_data[i*18-18+0] == selectedBuilding-1) data += "<a class='selFacLink' title='selected Building'>+ "+building_data[(i*18-18+4)] + "</a><br/>";
            else
            {
                if( isAllowed(building_data[i*18-18+3],building_data[i*18-18+1],building_data[i*18-18+2])) data += givelink(i,building_data[(i*18-18+4)]) + "<br/>";
                else data += "<a class='faclinkr' title='"+notAllowedReason+"''>- "+building_data[(i*18-18+4)] + "</a><br/>";
            }
      }
    }
    data += "</td></tr></table>";
    facList.innerHTML = data;

    lastTab = curTab;
    lastFullTab =tab;
}

//get the preferred image for the id.
//this function is useful to work around swc standard images. some images just.. suck :)
function findImage(buildingId)
{
if (buildingId == 20) return 2; //power gen should have second image as default.

return 1;
}


//(try to) retrieve the code from the url, if it works, process the code
function getCodeFromURL()
{
    var strCode = window.location.href.split("#")[0].split("?code=")[1]; //the code.
    if(strCode)
        if(strCode.length > 1)
            readText(strCode);
}



//destroy the world as we know it.
function clearMap()
{
    city.innerHTML = "";
    if(shadow) { city.appendChild(shadow); city.insertBefore(coordTag,shadow)}//restore prospected building into city.
    calculateMaterials();
}

//write output text into the textfield textarea
//give a link to the city designer with the code in the url, if asURL.
function writeText(asURL)
{
    var text = "";
    var node;
    var nodes = city.childNodes;
    if(asURL) text = (window.location+"").split("#")[0].split("?code=")[0]+"?code=";
    for(var i = 0; i < nodes.length;i++)
    {
       node = nodes[i];
       //must be a building, and must not be the unplaced/prospected building.
        if((node.buildingId) && !(node == shadow))
        {

            switch (node.rotation%360)
            {
            case 0 : node.rotation = "h"; break;
            case 270 : node.rotation = "v"; break;
            default: break;
            }
            text += node.buildingId + "," + node.x/25 + "," + node.y/25 + "," + node.rotation + ";" + ((asURL) ?"":" ");
        }
    }
    text += "|"+terrainType +"|"+((hidden)?"h":"v"); //append terrain and hidden-status.
    var link = gE("linkfield");

    gE("textfield").value =text; link.innerHTML ="";
    /*if(!asURL) { gE("textfield").value =text; link.innerHTML ="";}
    else { link.href =text; link.innerHTML=text.replace(";","; "); }*/
}


//function to parse text.
function readText(text)
{
    if(text.indexOf("code=") >= 0){
        text = text.split("code=")[1];
    }
    text = text.replace(" ","").replace("%20","");

    var textBuildings = text.split("|")[0].split(";");
    if((city.childNodes.length > 3) && !confirm("The currently displayed city will be deleted.\n continue?")) return;

    //restore terrain and hidden status.
    if((text.split("|")[1])) {
    setTerrain(text.split("|")[1]);
    hidden =  (text.split("|")[2].charAt(0) == 'h')?true:false;

    gE("hiddencheck").checked =hidden; //set hidden checkbox correctly, or confusion will ensue.
    }

    clearMap();

    for(var i = 0; i < textBuildings.length; i++)
    {
        if(textBuildings[i].length > 2 )
        if(!parseBuilding(textBuildings[i])) return alert('Warning. Design not Fully loaded! \n Unable to load '+ord(i+1)+' facility from code due to a syntax error.\n\n Fix the design and reload it. \n Details: \n '+error);
    }

    calculateMaterials(); //done here, in one go.
    setTimeout( function(){ ValidateBuildings();},100);
    newShadow(selectedBuilding,true);
    gE("textfield").value = "";
}

//1st, 2nd, 3d 4th
function ord(nr)
{
    switch(nr)
    {
        case 1: return '1st';
        case 2: return '2nd';
        case 3: return '3rd';
        default: return nr+'th';
    }
}

//parse an individual building "1,10,10,h" for instance (normally oriented personal residence at coords 10,10)
//return false if doesnt work.
function parseBuilding(text)
{
   try {
    var building;
    var x,y;
    var rotation;
    var tokens;

    tokens = text.split(",");

    building = findInternalBuildingId(parseInt(tokens[0]))+1; //+1. due to the way the array is accessed.

    if( ( !typeof(tokens[1]) == 'number') || !isFinite(tokens[1]) ) throw 'horizontal coordinate not numeric:'+tokens[1];
    if( ( !typeof(tokens[2]) == 'number') || !isFinite(tokens[2]) ) throw 'vertical coordinate not numeric:'+tokens[2];

    x = parseInt(tokens[1])*25;
    y = parseInt(tokens[2])*25;


    rotation = parseInt(tokens[3].replace("v1","270").replace("h1","0").replace("v","270").replace("h","0"));

    if(!(rotation == 0 || rotation == 270)) throw 'Rotation invalid, must be either \'v\' or \'h\' ';

    createBuilding(building,x,y,rotation,false); //but do not execute updatemats
    }
    catch(e) { error = e + '\n\n(in code:\''+text+'\' )'; return false; }
    return true;
}

//SWC/OUTPUT format to internal array ID. (as in building_data .js file.)
function findInternalBuildingId(realId)
{
    //  0,1,2,3			,4   ,5  ,6  ,7   ,8  ,9  ,10 ,11 ,12 ,13 ,14 ,15
   //,id,w,h,BuildingId,name,pow,Job,flat,Mel,dur,rud,lom,Var,Tib,Qua   ; 3= realId, 0 = internal ID.
    for(var i =1;i < building_data.length;i++)
    {
      if( realId == building_data[(i*18-18+3)] )
        return building_data[(i*18-18+0)];
     }
    throw 'Unknown Building ID: \''+realId+'\' \n(it may have changed, if this code is old?)';
}


//var prevCoord;

//acquire and store mouse coordinates + update shadow+coordtag.
function getMouseXY(e)
{
    //acquire mouse coordinates.
    if(IE)
    {
        mouseX = event.clientX + document.body.scrollLeft-cityX+13;
        mouseY = event.clientY + document.body.scrollTop-cityY+13;
    }
    else
    {
        mouseX = e.pageX-cityX+13;
        mouseY = e.pageY-cityY+13;
    }
   focusOutCity = false;


    shadow.x = Math.floor( ((mouseX)-shadow.halfw)/25 )*25; //snap to grid.
    shadow.y = Math.floor( ((mouseY)-shadow.halfh)/25 )*25;

    if( !(shadow.x == lastX && shadow.y == lastY) ) //if situation changed, i.e. new square.
    {
        shadow.style.left = shadow.x + "px";
        shadow.style.top = shadow.y + "px";
        shadow.x1 = shadow.x+shadow.w;
        shadow.y1 = shadow.y+shadow.h;

        updateCoordTag();
    }
    shadowCollisionCheck(); //red out if building won't fit.
    lastX = shadow.x; lastY = shadow.y;

}

var didfit = -2; //dit the building fit last time?
//refresh :
function shadowCollisionCheck(refresh)
{
    //if not necessarily need to refresh; dont refresh for the same position
    if( (!refresh) && (shadow.x == lastX) && (shadow.y == lastY) ) return;//+trace("unchanged",true);

    var fitting = buildingFits(shadow);

    //overlap
     if ((fitting == 0))
        {
         if( (refresh) || (didfit != fitting) )
            {
                shadow.style.backgroundColor= "#FF0000";
                shadow.style.background = "red";
                shadow.innerHTML = "";
            }
            didfit = fitting;
        }
    //not 2 sides free for building, or neighbour.
    else if ((fitting == -1))
        {
         if( (refresh) || (didfit != fitting) )
            {
                shadow.style.backgroundColor= "#FFFF00";
                shadow.style.background = "orange";
                shadow.innerHTML = "";
            }
            didfit = fitting;
        }
    //neither of above: will fit.
     else
        {
            if( (refresh) || (didfit != fitting) )
            {
                shadow.style.backgroundColor= "#003366";
                shadow.innerHTML = "<img src='"+getImageURL(shadow.buildingId,shadow.rotation)+"'  height='"+shadow.h+"' width='"+shadow.w+"'   />";
                didfit = fitting;
            }
        }
}


//this function can be used to assign non-standard images to certain facilities.
function getImageURL(buildingId,rotation)
{
//if(parseInt(buildingId)==18) return imageURL+"basement_25_4.gif";  //old exception for roads. quite proves usefulness.

return imageURL+buildingId+((rotation+'').replace('270','/vertical_1').replace('0','/horizontal_1') )+'.gif';

}

//return true, unless suggested SWC ID is within the list
//used for exceptions for walls/ roads
function needsRoads(buildingId)
{
    buildingId = parseInt(buildingId);
    for(var i = 0; i < noRoadFacilities.length; i++) {
        if(buildingId == noRoadFacilities[i])
            return false;
    }
    return true;
}


//debug, some useful info in the collision checks, outputs useable info.
function describeNode(node)
{
var text = '';
if(!node) text = 'Node not defined?'
else {
text = "facility"+ node.facData.name;
text += "\n x,y: " + node.x/25 + "," + node.y/25;
text += "\n x1,y1: " + node.x1/25 + "," + node.y1/25;
}

return text;
}

//abbreviation functions, used in optimalisation, avoiding setting string always.
function setrsn() { notAllowedReason = "Encroaches/Overlaps another building"; return 0; }
function setrsn2() { notAllowedReason = "is not within city limits.";  return 0; }

//returns int indicating wether it will fit: 1 = fits. 0 = doesnt fit. -1 = would defy 2 sides free rule.
//todo: optimize this please. (yes, I am talking to myself.)
//if validating, perform extra check to exclude shadow.
function buildingFits(node)
{

    //immediately disqualify if does not fit city limits.
    if(node.x+cityX < cityX) return 0+setrsn2();
    if(node.y+cityY < cityY) return 0+setrsn2();
    if(node.x1+cityX > cityX1) return 0+setrsn2();
    if(node.y1+cityY > cityY1) return 0+setrsn2();



    buildings = city.childNodes;
    var build; //current building in for loop.

    for(var i = 0; i < buildings.length; i++)
    {
        build = buildings[i];

        //shadow used to not be in here, used to acquired from be name attribute, which DOES NOT WORK IN IE.
        //method now revised to take any node for checking (used to be just 'shadow')

        if((build != shadow) && (build.buildingId) && (node != build)  )
        {

        //check if there is at all a chance that the buildings can intersect/hit, before expensive operation.
        if( !( (build.x > node.x1) || (build.y > node.y1) || (node.x > build.x1) || (node.y > build.y1)  ) )
        {
        //8 pairs of 2 if staments, check to see if any of the corners of both rectangles are within other rectangle

        //seen from prospected building


        if( (node.y >= build.y)  && (node.y < build.y1)) //top
        {
            if( (node.x >= build.x)  && (node.x < build.x1))
                return false + setrsn();//+alert("top-left");

            if( (node.x1 > build.x)  && (node.x1 <= build.x1))
                return false + setrsn();//+alert("top-right");
        }

         if( (node.y1 > build.y)  && (node.y1 <= build.y1)) //bottom
        {
            if( (node.x1 > build.x)  && (node.x1 <= build.x1))
                return false + setrsn();//+alert("bottom-right");
            if( (node.x >= build.x)  && (node.x < build.x1))
                return false + setrsn();//+alert("bottom-left");
        }

        //as seen from existing building
        if( (build.x >= node.x)  && (build.x < node.x1)) //left
        {
            if( (build.y >= node.y)  && (build.y < node.y1))
                return 0 + setrsn();//+alert("top-left");
            if( (build.y1 > node.y)  && (build.y1 <= node.y1))
                return 0 + setrsn();//+alert("bottom-left");
        }

        if( (build.x1 > shadow.x)  && (build.x1 <= node.x1)) //right
        {
            if( (build.y1 > node.y)  && (build.y1 < node.y1))
                return 0 + setrsn();//+alert("bottom-right");


            if( (build.y > node.y)  && (build.y < node.y1))
            {
                return 0 + setrsn();//+alert("top-right");
                }
        }

        //avoid the bodies of the buildings overlapping, even if the corners do not lie within the other building.
           if(node.x < build.x)
        if(node.x1 > build.x1)
        if(node.y > build.y)
        if(node.y1 < build.y1)
        return 0 + setrsn();


        if(build.x < node.x)
        if(build.x1 > node.x1)
        if(build.y > node.y)
        if(build.y1 < node.y1)
        return 0 + setrsn();

        }
        }
    }
    //if the user has allowed the two sides free check.
    if(check2Sides)
    {
        if(buildingFitsProperly(shadow,true)) return 1; // will  fit.
        else return -1;
    }
    return 1;


}


//useful debug function
function trace(htmlText,clear)
{
    try
    {
        if(clear) facList.innerHTML = "";
        facList.innerHTML += htmlText;
    }
    catch(e) { ; }
}

//does the given building have 2 sides free?
//if recurse is set to true, the 'neighbours' of the target will be asked if they too will be ok, via a recursion step.
function buildingFitsProperly(target,recurse,ignoreShadow)
{
    //if(target.buildingId == 18) return true; //temporary fix for road, always fits. roads are no longer in the city designer.
    var roadNeed = needsRoads(target.buildingId);
    //trace(roadNeed);
  if(!recurse && !roadNeed) return true;


    buildings = city.childNodes;
    var build; //current building in for loop.

    var north = 0;
    var south = 0;
    var east = 0;
    var west = 0;

    //regard facilities placed on borders as lacking space on those sides.
    if(roadNeed)
    {
        if(target.x+cityX-25 < cityX) west=1;
        if(target.y+cityY-25 < cityY) north=1;
        if(target.x1+cityX+25 > cityX1) east=1;
        if(target.y1+cityY+25 > cityY1) south=1;
    }

    for(var i = 0; i < buildings.length; i++)
    {
        var bump = 0; //does current build bump into target?
        build = buildings[i];

        //if the shadow should be ignored, do nothing this iteration.
        if( (ignoreShadow) && (build == shadow) ) ; //else: normal.
        else

        if( (build != target) && (build.buildingId) ) //old exception: && (!(build.buildingId == 18) )- fix for roads.
        {
           //check if there is even a chance that they hit, if so, only then do expensive calculation.
           if( !( (build.x > target.x1) || (build.y > target.y1) || (target.x > build.x1) || (target.y > build.y1) )  )
           {
            if( (target.y >= build.y)  && (target.y < build.y1+25)) //(target.y < build.y1+25))upper/north
            {
                if( (target.x >= build.x)  && (target.x1 < build.x1))
                {
                    north = 1;
                    bump =1;
                }
                else if( (target.x1 > build.x)  && (target.x < build.x1))
                {
                    north = 1;
                    bump =1;
                }
            }

            if( (target.y1+25 > build.y)  && (target.y1 <= build.y1)) //(target.y1+25 > build.y) lower/south
            {
                if( (target.x1 > build.x)  && (target.x < build.x1))
                {
                    south = 1;
                    bump =1;
                }
                else if( (target.x >= build.x)  && (target.x1 < build.x1))
                {
                    south = 1;
                    bump =1;
                }
            }

            if( (target.x1 > build.x-25)  && (target.x1 < build.x1)) //right east
            {
                if( (target.y >= build.y)  && (target.y1 < build.y1))
                {
                    east = 1;
                    bump =1;
                }
                else if( (target.y1 > build.y)  && (target.y < build.y1))
                {
                    east = 1;
                    bump =1;
                }
            }

            if( (target.x >= build.x)  && (target.x < build.x1+25)) //left west
            {
                if( (target.y1 > build.y)  && (target.y < build.y1))
                {
                    west = 1;
                    bump =1;
                }
                else if( (target.y >= build.y)  && (target.y1 < build.y1))
                {
                    west = 1;
                    bump =1;
                }
            }
            //if(!recurse) trace("n"+north+"s"+south+"e"+east+"w"+west,true); //useful debug.
            //needs roads, but doesnt have 2 sides free.
            if( (roadNeed) && (north+south+west+east > 2)) {notAllowedReason = "Facility will not have enough road for itself."; return false;}

            //if the checked building was a neighbour, does it have 2 sides free?
            var neighbourAgrees = true;
            if((recurse) && (bump)) {
            neighbourAgrees = buildingFitsProperly(build,false); //no future recursion.
            if(!neighbourAgrees) {notAllowedReason = "Neighbour facility cannot have enough roads."; return false}; //respect thy neigbour.
            }
           }
        }
    }
   return true;
}


//calculate and display total material count.
function calculateMaterials()
{
    powTot = jobTot = flatTot= melTot = durTot = rudTot =
    lomTot = varTot = tibTot = quaTot = bacTot = 0;

    buildings = city.childNodes;
    for (var i = 0;i < buildings.length;i++)
    {
        //unnecessary before I stepped away from the getElementsByName.. rotten IE!
        if((buildings[i].facData) && (buildings[i] != shadow))
        {
            var data = buildings[i].facData;
            powTot	+=data.pow;
            jobTot	+=data.job;
            flatTot	+=data.flat;
            melTot	+=data.mel;
            durTot	+=data.dur;
            rudTot	+=data.rud;
            lomTot	+=data.lom;
            varTot	+=data.vari;
            tibTot	+=data.tib;
            quaTot	+=data.qua;
            bacTot	+=data.bac;
        }
    }
    refreshMaterials();
}

//updates material counters.
function refreshMaterials()
{
    if(powTot < 0) { powTag.style.color = "#FF0000"; powTag.title="Insufficient Power for all facilities."; }
    else { powTag.style.color = "#00FF00"; powTag.title="Power requirements met."; }
    powTag.innerHTML = powTot;
    jobTag.innerHTML = Math.round(jobTot*100)/100; //2 decimal accuracy.- damn parks.
    flatTag.innerHTML = flatTot;

    var er = Math.round(100* (jobTot/(flatTot+0.1)) )/100; //2 decimal accuracy.

    if( (er > 2.0) || (er < 1.0 ) ) { erTag.style.color = "#FF0000"; erTag.title = "Unbalanced for city. \n However ER is a planetary stat."; }
    else  { erTag.style.color = "#00FF00"; erTag.title = "Balanced for city. \n note: ER is a planetary stat."; }

    erTag.innerHTML = er;
    melTag.innerHTML = melTot;
    durTag.innerHTML = durTot;
    rudTag.innerHTML = rudTot;
    lomTag.innerHTML = lomTot;
    varTag.innerHTML = varTot;
    tibTag.innerHTML = tibTot;
    quaTag.innerHTML = quaTot;
    bacTag.innerHTML = bacTot;

    volTot = melTot+durTot+rudTot+lomTot+varTot+tibTot+quaTot+bacTot;
    masTot = melTot*14+durTot*12+rudTot*2+lomTot*8+varTot*13+tibTot*0.2+quaTot*16+bacTot*1;
    volTag.innerHTML = volTot;
    massTag.innerHTML = masTot;
    refreshFreighterHauls();
}

//calculate new total amount of materials, including the new one.
//if addition is false, then the new materials are subtracted from the total.
function updateMaterials(facData,addition)
{
    var data = facData;
    var mod = -1;
    if (addition) mod = 1;

    powTot	+=data.pow*mod;
    jobTot	+=data.job*mod;
    flatTot +=data.flat*mod;
    melTot	+=data.mel*mod;
    durTot	+=data.dur*mod;
    rudTot	+=data.rud*mod;
    lomTot	+=data.lom*mod;
    varTot	+=data.vari*mod;
    tibTot	+=data.tib*mod;
    quaTot	+=data.qua*mod;
    bacTot	+=data.bac*mod;

    if(IE) clearTimeout(statdelaytimer);
    statdelaytimer = setTimeout(refreshMaterials,150);
}


//changes variables describing freighter.
function changeFreighter()
{

    freighter.name = freighterList.value;
    for(var i = 1; i <= frData.length/3;i++)
    {
        if(freighter.name == frData[i*3-3])
        {
          freighter.volCap =  frData[i*3-2];
          freighter.weightCap =  frData[i*3-1];
        }
    }

    if(!volTot) { volTot = 0; masTot = 0; }
    refreshMaterials(); //refresh the list of materials, which then also refreshes freighter stats.
}


//refreshes amount of hauls needed for the currently selected freighter.
function refreshFreighterHauls()
{
    //calculate statistics.
    var hauls = Math.ceil(Math.max(volTot/freighter.volCap, masTot/freighter.weightCap));

    freighter.volPercent = 		Math.round(((volTot/hauls)/freighter.volCap) *10000 )/100;
    freighter.weightPercent = 	Math.round(((masTot/hauls)/freighter.weightCap) *10000)/100;


    //alert(freighter.weightCap);
    //update statistics, only if useful. meaning none of the factors may be 0
    //alert(freighter.weightCap);
    if( (volTot+masTot != 0) && (freighter.weightCap != 0) && (freighter.volCap != 0) )
    {

        haulTag.innerHTML = hauls+" haul"+ ((hauls >1)?"s":"");
        capTag.innerHTML = '(Mass: '+freighter.weightPercent+'%   '+
        'Volume: '+freighter.volPercent+'%)';


        massTag.innerHTML += "/" + freighter.weightCap ;
        volTag.innerHTML += "/" + freighter.volCap;
    }
    else
    {
        haulTag.innerHTML = 'no haul';
        capTag.innerHTML = 'none';
    }

    //freighterList.title = "Vol. :"+freighter.volCap + " , Mass: "+freighter.weightCap;
}



//on mouse click, make the shadow a permanent building.
//also: if the mouse click was within the limits of an existing building, remove this building.
function newBuilding()
{
   keyVal = 666; //clicked, so situation under mouse has changed since last key-event.
   if(buildingFits(shadow) == 0) { remove(); return;}
   else if ((check2Sides) && (buildingFits(shadow) == -1) ) return;

    createBuilding(shadow.id,shadow.x,shadow.y,shadow.rotation,true);
    newShadow(selectedBuilding,true);
}

function placeBuilding()
{
   if(buildingFits(shadow) != 1) { return;}

    createBuilding(shadow.id,shadow.x,shadow.y,shadow.rotation,true);
    newShadow(selectedBuilding,true);

}


//make a new shadow that corresponds to selected building.
//if no building is selected, no new shadow is returned: instead null/void/nada.
// NOTE/WARNING: shadow is only added if autoAdd == true.
function newShadow(selectedId,autoAdd)
{

   try{
    city.removeChild(shadow); //dispose of old div (code may still reference it before GC kills it.)
    }
    catch(e) {;}
    if(selectedId == null) return;


    //create and place in document's body.
    createdShadow = document.createElement("div");
    createdShadow.style.cursor = "move";
    createdShadow.id = selectedId
    createdShadow.buildingId = createdShadow.id;
    createdShadow.rotation = rotation;

    //position related. the x and y attributes are assigned by getMouseXY.
    var facData = getFac(createdShadow.buildingId);
    createdShadow.facData = facData;
    createdShadow.buildingId = facData.id;

    //switch width and height if the building is rotated vertically.
    if(!(rotation %180))
    {
        createdShadow.w = facData.width;
        createdShadow.h = facData.height;
    }
    else
    {
        createdShadow.h = facData.width;
        createdShadow.w = facData.height;
    }

    createdShadow.halfw = createdShadow.w/2;
    createdShadow.halfh = createdShadow.h/2;

    createdShadow.style.width= createdShadow.w+"px";
    createdShadow.style.height= createdShadow.h+"px";
    createdShadow.x1 = createdShadow.x+createdShadow.w; //useful for collision detection simplification.
    createdShadow.y1 = createdShadow.y+createdShadow.h; //same.

    //normal visual representation. (if image has not yet been loaded :blue)
    createdShadow.style.backgroundColor= "#003366";

    createdShadow.style.position= "absolute";
    createdShadow.style.opacity="0.5";
    if(IE) createdShadow.style.filter = "alpha(opacity=50)"
    //automatically add to the city
    if (autoAdd)
    {
        if(mouseX > cityX1) mouseX = -400; //hide if mouse not in city.
        if(mouseY > cityY1) mouseY = -400;

        createdShadow.x = Math.floor( ((mouseX)-createdShadow.halfw)/25 )*25;; //set cursor position according to cursor, snap to grid.
        createdShadow.y = Math.floor( ((mouseY)-createdShadow.halfh)/25 )*25;
        createdShadow.style.left = createdShadow.x+"px";
        createdShadow.style.top = createdShadow.y+"px";
        createdShadow.x1 = createdShadow.x +createdShadow.w; 	//for collision checks.
        createdShadow.y1 = createdShadow.y +createdShadow.h;

        shadow = createdShadow; //possibly double, but safe and convenient.
        city.appendChild(createdShadow);
    }
    else
    {
        createdShadow.x = -500; //hide at first.
        createdShadow.y = -100;
    }
    //didfit = 0; //assume it will not fit. causes appropiate logic to spark when needed.
    shadowCollisionCheck(true); //execute first-time-like check.
    return createdShadow;
}

