/*
 * routefinder.js
 *
 * Global Animated Route Finder 
 * 
 * Author:	Matthias Braun
 * Version: 0.9x Server
 */

/*
 * Data Bean. Simple point.
*/
function Point2D(x, y) {
	this.x = x;
	this.y = y;
}

/*
 * Represents an image with positioning information (x,y).
 * 
 */
function MapImage(imgSrc, x, y) {
	this.imgSrc = imgSrc;
	this.x = x;
	this.y = y;
	
	if(x == undefined || y == undefined) {
		this.x = 0;
		this.y = 0;
	}
	
	this.img = null;		// image object
	this.imgInit = false;
	
	/*
	 * Optional parameters (fadeIn, imgRef) can be used to create a smooth fade in effect
	 * after loading of the image is complete.
	 *  
	 * fadeIn: time in ms
	 * imgRef: html image id 
	 * 
	 */
	
	this.loadImage = function(fadeIn, imgRef) {
		this.img = new Image();
		
		if(fadeIn != null && fadeIn != undefined && imgRef != null && imgRef != undefined) {
			
			$(this.img).load(function () {	
				$(this).hide();
				$(imgRef).fadeIn(fadeIn);
			});
		}
		
		$(this.img).attr("src", this.imgSrc);
  		this.imgInit = true;
  	}
}

/*
 * DataBean. Represents a service route for a specific direction.
 * 
 * srcRegionPorts: array of loadingPort names on route
 * trgRegionPorts: array of dischargePort names on route
 * transit table: transit time matrix (2D-array)
 * mapImage: service chart image 
 */
function Route(srcRegionPorts, trgRegionPorts, transitTable, mapImage) {
	
	this.srcRegionPorts = srcRegionPorts;
	this.trgRegionPorts = trgRegionPorts;
	
	this.arrivalDays = null;
	
	this.transitTable = null;	
	this.mapImage = mapImage;
	
		
	this.addSourcePort = function(portName) {
		if(this.srcRegionPorts == null) {
			this.srcRegionPorts = new Array();
		}
		this.srcRegionPorts.push(port);
	}
	
	this.addTargetPort = function(portName) {
		if(this.trgRegionPorts == null) {
			this.trgRegionPorts = new Array();
		}	
		this.trgRegionPorts.push(port);	
	}
	
	this.setArrivalDays = function(strDays) {
		if(this.trgRegionPorts != null && (this.trgRegionPorts.length == strDays.length)) {
			this.arrivalDays = strDays;
		}
	}
	
	/**
	 * Sets the transit time for a srcPort - trgPort pair.
	 * Source port is specified by index i.
	 * Target port is specified by index j.
	 * 
	 * time	specifies the transit time.
	 */
	this.setTransitTimeEntry = function(i, j, time) {	
		if(this.transitTable != null) {	
			if(i >= 0 && i < this.transitTable.length) {
				var row = this.transitTable[i];
				if(j >= 0 && j < row.length) {
					row[j] = time;
				}
			}
		}	
	}
	
	this.initTransitTimeMatrix = function() {
				
		if(this.srcRegionPorts != null && this.trgRegionPorts != null) {	
			// Create rows array
			this.transitTable = new Array(this.srcRegionPorts.length);
			
			// Create col arrays for each row
			for(i = 0; i < this.transitTable.length; i++) {	
				var array = new Array(this.trgRegionPorts.length);
				for(j = 0; j < array.length; j++) {
					array[j] = "";
				}
				this.transitTable[i] = array;	
			}	
		}	
	}
	
	/**
	 * Updates the transit times specified in the matrix.
	 * matrix[i][j] = value
	 * 
	 * i: index of srcPort in the srcPortList
	 * j: index of trgPort in the trgPort List
	 * 
	 * the first row in the matrix specifies all transit times from the
	 * first srcPort to all destination ports.
	 */
	this.setTransitTimeMatrix = function(matrix) {
		
		var row = null;
		
		for(i = 0; i < matrix.length; i++) {
			
			row = matrix[i];
				
			for(j = 0; j < row.length; j++)
				this.setTransitTimeEntry(i, j, row[j]);
			}	
	}
	
	// Constructor
	
	if(this.srcRegionPorts == null) {
		this.srcRegionPorts = new Array();
	}
		
	if(this.trgRegionPorts == null) {
		this.trgRegionPorts = new Array();
	}	
	
	this.initTransitTimeMatrix();
	
	if(transitTable != null) {
		this.setTransitTimeMatrix(transitTable);
	}

}

/*
 * DataBean. Represents a service.
 * A service is decribed by:
 * 
 * name: short name (abbreviation) shown in the list.
 * westboundRoute: west route (image + transit times)
 * eastboundRoute: east route (image + transit times)
 * fisCode: used for bookmarking and connection to interactive schedule
 */
function Service(name, longName, westboundRoute, eastboundRoute) {
	this.name		= name;
	this.longName 	= longName;
	this.fisCode 	= null;
	this.ssyCode 	= null;
	this.reverseDirectionCode = false;
	
	this.eastRoute = eastboundRoute;
	this.westRoute = westboundRoute;
	
	this.setFisCode = function(code, isReverseDirection) {	
		this.fisCode = code;	
		if(isReverseDirection == true)
			this.reverseDirectionCode = isReverseDirection;	
	}
	
	this.setSsyCode = function(code) {	
		this.ssyCode = code;
	}
	
	this.toString = function() {	
		return this.name;	
	}
}

/*
 * 
 * Data Bean. Represents a service map, i.e., a region combination.
 * Example: NorthAmerica-NorthEurope.
 * 
 * mapImage: base image of service map -> shown if no service is selected.
 * regionFrom: left region of service map image
 * regionTo: right region of service map image
 * services: all services contained in this region combination.
 */
function ServiceMap(mapImage, regionFrom, regionTo, services) {
	this.mapImage = mapImage;
	this.regionFrom = regionFrom;
	this.regionTo = regionTo;
	this.services = services;
	
	this.url = null;	
	this.westbound = true;
	
	
	if(services == null || services == undefined) {
		
		this.services = new Array();
	}

	this.addService = function(service) {	
		if(this.services == null) {		
			this.services = new Array();		
		}	
		this.services.push(service);	
	}
	
	this.setURL = function(url) {	
		this.url = url;	
	}	
}

/*
 * DataBean used by the worldMap Controller.
 * Contains world image and all defined regions.
 * Contains all defined region combinations (servicemaps).
 */
function WorldMap(mapImage, regions, serviceMaps) {
	this.mapImage = mapImage;
	this.regions = regions;
	this.serviceMaps = serviceMaps;
		
	// public methods
	this.addServiceMap = function(serviceMap) {	
		if(this.serviceMaps == null) {		
			this.serviceMaps = new Array();		
		}	
		this.serviceMaps.push(serviceMap);	
	}
	
}

//Contains all text messages
function Messages() {
		
	// worldmap
	
	// Status line on top
	this.MSG_SRC_REGION = "1. Please select a source region...";
	this.MSG_TRG_REGION = "2. Please select a target region...";
	
	// Map header
	this.MSG_WORLD_MAP_DEFAULT = "Choose Regions:  ";
	this.MSG_WORLD_MAP_SELECTION_PRE = "Choose Regions:  ";
	this.MSG_WORLD_MAP_NO_SELECTION_MIDDLE = "  to  ";
	this.MSG_WORLD_MAP_TARGET_DEFAULT = "...";
	
	this.MSG_WORLD_MAP_BTN_RESET_SELECTION = "Reset Selection";
	
	
	// service map
		
	// Direction change button
	this.MSG_SERVICE_MAP_BTN_DIRECTION_WEST = "Switch Direction";
	this.MSG_SERVICE_MAP_BTN_DIRECTION_EAST = "Switch Direction";
	
	// Schedule button
	this.MSG_SERVICE_MAP_BTN_SCHEDULE_SERVICE = "Show Schedule by Service";
	this.MSG_SERVICE_MAP_BTN_SCHEDULE_REGION = "Show Schedule by Region";
	
	// Reset button
	this.MSG_SERVICE_MAP_BTN_RESET_SELECTION = "Reset Selection";
	
	// Status line on top
	this.MSG_SERVICE_MAP_NO_SELECTION = "Please select a service...";
	this.MSG_SERVICE_MAP_SERVICE_SELECTION = "Please select a service...";
		
	// Map Header
	this.MSG_SERVICE_MAP_HEADER_MAP_PRE = "Chosen Regions:  ";
	this.MSG_SERVICE_MAP_HEADER_MAP_MIDDLE = "  to  ";
	this.MSG_SERVICE_MAP_HEADER_MAP_SERVICES_FROM = "Services from ";
	
	// Table header
	this.MSG_SERVICE_MAP_TABLE_FROM_TO = "From/To";
	//this.MSG_SERVICE_MAP_TABLE_ARRIVAL_DAY = "Arrival Day";
	
	// Text for interactive schedule box
	this.MSG_SERVICE_MAP_SCHEDULE_SELECTION_REGION = "Go to interactive schedule with your selected regions or service";
	this.MSG_SERVICE_MAP_SCHEDULE_SELECTION_SERVICE = "Go to interactive schedule with your selected service or service";
	
	// Text if no FIS code is set
	this.MSG_SERVICE_MAP_NO_SCHEDULE_LINK = "This service is not available in the interactive schedule";
}


/*
 * Data Bean. Represents a world region.
 * 
 * name: region name.
 * mapImage: image that appears when hovering over a region on the worldmap. 
 */
function Region(id, name, mapImage) {
	
	this.id = id;
	this.name = name;
	this.mapImage = mapImage;
	
	if(mapImage == undefined) {
		this.mapImage = null;
	}
	
	this.fisCode = null;
	 
  	// public methods
 	
 	this.setFisCode = function(code) {	
		this.fisCode = code;		
	} 
  	this.toString = function() {
 		return this.name;
	}
}

/*
 * Controller used for the worldmap template.
 * 
 */
function ControllerWorldMap() {
	
	// constants for application
	this.C_APP_PROD = 0;
	this.C_APP_SMART_EDIT = 1;
	
	// constants for selection mode
	this.C_NONE = 0;				// no region is selected
	this.C_SRC = 1;					// src region has been selected
	this.C_TRG = 2;					// target region has to be selected
	this.C_FINISHED = 3;			// target region has been selected
	
	// constants for direction
	this.C_WEST = 0;				
	this.C_EAST = 1;
	
	this.C_PARAM_DIRECTION = "dir";		// url parameter for direction
	
	this.C_TARGET_REGION_ALPHA = 0.1;	// transparency for target regions
	
	// model
	this.worldMap = null;
	this.serviceMaps = null;
	this.regions = null;
	
	this.selectedSrcRegion = null;
	this.selectedTrgRegion = null;
	
	this.targets = new Array();			// potential target regions
	this.msg = new Messages();			// messages used
	
	this.appMode = this.C_APP_PROD;
	this.selectMode = this.C_SRC;
		
	this.ajaxLoader = null;				// ajaxLoader image, not used
	
	// Gui components
	this.guiStatusField = "div#grfStatusLine";		// Status Line
	this.guiMapContainer = new GuiMapContainer("div#grfMapContainer", this.worldMap);
	this.guiList = new GuiList("ul#grfNavi");
			
	// Button
	this.guiBtnResetSelection = "input#btnResetSelection";
	
	// Map Header
	this.guiMapHeadSingleText = "h2#mapSingleText";
	
	this.init = function(worldMap) {
			
		// set data	
		this.worldMap = worldMap;
		this.serviceMaps = worldMap.serviceMaps;
		this.regions = worldMap.regions;
		
		// init MapContainer
		this.guiMapContainer.setEventListener(this);
		this.guiMapContainer.init();
		this.guiMapContainer.setWorldMap(worldMap);		
		this.guiMapContainer.loadWorldMap();
	
		// init guiList that contains all regions
		this.guiList.setEventListener(this);
		this.guiList.init();
		this.guiList.setEntries(this.regions);
		
		this.setStatusLineText(this.msg.MSG_SRC_REGION);
		// set map header
		this.setMapHeaderText(this.msg.MSG_WORLD_MAP_DEFAULT);
		
		$(this.guiBtnResetSelection).attr("value",this.msg.MSG_WORLD_MAP_BTN_RESET_SELECTION);
		$(this.guiBtnResetSelection).hide();
	}
	
	// btnResetSelection onclick handler
	this.evtGuiBtnResetSelectionClick = function(){		
		this.deselectAllRegions();	
	}
	
	this.evtGuiListHoverIn = function() {	
	}
	
	this.evtGuiListHoverOut = function() {	
	}
	
	// Connection to event handler
	this.evtGuiListEntryHoverIn = function(index, object) {	
		
		if(this.selectMode != this.C_FINISHED) {		
			this.handleRegionHoverIn(index, this.regions[index]);
		}
	}
	
	// Connection to event handler
	this.evtGuiListEntryHoverOut = function(index, object) {	
		
		if(this.selectMode != this.C_FINISHED) {	
			this.handleRegionHoverOut(index, this.regions[index]);
		}	
	}
	
	// Connection to event handler
	this.evtGuiListEntryClick = function(event, index, object) {
		if(this.selectMode != this.C_FINISHED) {	
			this.handleRegionClick(event, index, this.regions[index]);
		}	
	}
	
	// Connection to event handler
	this.evtGuiMapContainerHoverIn = function(index, srcType, object) {	
		if(srcType == this.guiMapContainer.C_REGION) {
			if(this.selectMode != this.C_FINISHED) {	
				this.handleRegionHoverIn(index, object);
			}	
		}	
	}
	
	// Connection to event handler: Captures hover events within the map
	this.evtGuiMapContainerHoverOut = function(index, srcType, object) {		
		// region hover
		if(srcType == this.guiMapContainer.C_REGION) {
			if(this.selectMode != this.C_FINISHED) {	
				this.handleRegionHoverOut(index, object);
			}				
		}	
	}
	
	this.evtGuiMapContainerClick = function(event, index, srcType, object) {			
		// User selects a region on the world map	
		if(this.selectMode != this.C_FINISHED) {	
			if(srcType == this.guiMapContainer.C_REGION) {
				this.handleRegionClick(event, index, object);
			}					
		
			// User clicks on the base map (service map or world map)
			else if (srcType == this.guiMapContainer.C_BASE_MAP) {		
				// User clicks on world map
				this.deselectAllRegions();
			}
		}
	}
	
	// Event handler functions
	
	this.handleRegionHoverIn = function(index, region) {
			
		// source region		
		if(this.selectMode == this.C_SRC) {
			this.guiMapContainer.showRegion(index);
			this.guiList.highlightEntry(index);
		}
		// target region
		else if(this.selectMode == this.C_TRG ) {
		
			var contained = false;
			for(i=0;i<this.targets.length;i++) {			
				if(this.targets[i] == region)
					contained = true;			
			}
			// target region is a valid target region
			if(contained) {
				this.guiMapContainer.showRegion(index);
				this.guiList.highlightEntry(index);
				// set map header		
				this.setMapHeaderText(this.msg.MSG_WORLD_MAP_SELECTION_PRE + this.selectedSrcRegion.name + 
										 this.msg.MSG_WORLD_MAP_NO_SELECTION_MIDDLE + region.name);
			}	
		}
	}
	
	this.handleRegionHoverOut = function(index, region) {
	
		// user has not selected a source region
		if(this.selectMode != this.C_TRG) {	
			this.guiMapContainer.hideRegion(index);	
			this.guiList.unhighlightEntry(index);
		}
		// user has selected a source region
		else if(this.selectMode == this.C_TRG) {
			
			var contained = false;
			for(i=0;i<this.targets.length;i++) {		
				if(this.targets[i] == region)
					contained = true;		
			}
			// region is potential target region
			if(contained && this.selectedSrcRegion != region) {
				this.guiMapContainer.showTransparentRegion(index, this.C_TARGET_REGION_ALPHA);
				this.guiList.unhighlightEntry(index);
			}
				// set map header
				this.setMapHeaderText(this.msg.MSG_WORLD_MAP_SELECTION_PRE + this.selectedSrcRegion.name + 
												  this.msg.MSG_WORLD_MAP_NO_SELECTION_MIDDLE + 
												  this.msg.MSG_WORLD_MAP_TARGET_DEFAULT);	
		}
	}
	
	this.handleRegionClick = function(event, index, region) {
	
		// src region has been selected
		if(this.selectMode == this.C_SRC) {
			
			this.selectMode = this.C_TRG;		
			this.selectedSrcRegion = region;
			
			this.setStatusLineText(this.msg.MSG_TRG_REGION);	
			this.guiList.selectEntry(index);				
			$(this.guiBtnResetSelection).show();
			// set map header							
			this.setMapHeaderText(this.msg.MSG_WORLD_MAP_SELECTION_PRE + region.name + 
											  this.msg.MSG_WORLD_MAP_NO_SELECTION_MIDDLE + 
											  this.msg.MSG_WORLD_MAP_TARGET_DEFAULT);
				
			this.guiMapContainer.showRegion(index);
			
			var reg = null;	
			for(i=0;i<this.targets.length;i++) {
				this.targets[i] = null;
			}
				
			// determine target regions
			for(i=0;i<this.serviceMaps.length;i++) {
				
				if(this.serviceMaps[i].regionFrom == this.selectedSrcRegion) {		
					this.targets.push(this.serviceMaps[i].regionTo);		
				}
				else if(this.serviceMaps[i].regionTo == this.selectedSrcRegion) {		
					this.targets.push(this.serviceMaps[i].regionFrom);	
				}	
			}
			
			// disable list entries for all non-target regions
			for(i=0;i<this.regions.length;i++) {		
				reg = this.regions[i];
				
				if(index != i) {			
					this.guiList.disableEntry(i);		
				}	
			}
			
			var intraRegion = false;	
			// highlight list entries for all target regions
			for(t=0;t<this.targets.length;t++) {	
				var tindex = this.getRegionIndex(this.targets[t]);
				
				if(this.selectedSrcRegion != this.targets[t]) {
					this.guiMapContainer.showTransparentRegion(tindex, this.C_TARGET_REGION_ALPHA);
					this.guiList.enableEntry(tindex);
				}
				else if(this.selectedSrcRegion == this.targets[t])
					intraRegion = true;
			}
			
			if(intraRegion) {
				// set map header
				this.setMapHeaderText(this.msg.MSG_WORLD_MAP_SELECTION_PRE + this.selectedSrcRegion.name + 
											  this.msg.MSG_WORLD_MAP_NO_SELECTION_MIDDLE + this.selectedSrcRegion.name);
						
			}
		
		}
		
		// target region has been selected
		else if(this.selectMode == this.C_TRG) {
		
			var contained = false;
			for(x=0;x<this.targets.length;x++) {
				if(this.targets[x] == region)
					contained = true;
			}
			
			// check special case
			if(!(this.selectedSrcRegion == region) || contained) {
			
				this.selectMode = this.C_SRC;		
				this.selectedTrgRegion = region;
				
				for(t=0;t<this.targets.length;t++) {					
					if(this.targets[t] != this.selectedTrgRegion) {
						var tindex = this.getRegionIndex(this.targets[t]);		
						this.guiMapContainer.hideRegion(tindex);
					}
				}
							
				// determine respective service map		
				var sMap = null;
			
				for(i=0;i<this.serviceMaps.length;i++) {
					
					// select service map between the corresponding regions	
					if((this.serviceMaps[i].regionFrom == this.selectedSrcRegion &&
						this.serviceMaps[i].regionTo == this.selectedTrgRegion)
						|| (this.serviceMaps[i].regionTo == this.selectedSrcRegion &&
							this.serviceMaps[i].regionFrom == this.selectedTrgRegion)) {
								
						sMap = this.serviceMaps[i];
					}			
				}

				// service map between the two regions exists
				if(sMap != null ) {
					
					this.selectMode = this.C_FINISHED;	
					this.guiMapContainer.showRegion(index);
					this.guiMapContainer.showRegion(this.getRegionIndex(this.selectedSrcRegion));
					
					// determine east bound or westbound direction
					if(this.selectedSrcRegion == sMap.regionFrom) {
						this.dispatchServiceMap(sMap, this.C_EAST);
					}
					else {		
						this.dispatchServiceMap(sMap, this.C_WEST);
					}
			
				}
				// service map does not exist --> unselect
				else{
					this.selectMode = this.C_TRG;
					this.selectedTrgRegion = null;
					this.deselectAllRegions();	
				}
			}				
		}
	}
	
	/*
	 * Returns index of region array for a specified region object
	 */
	this.getRegionIndex = function(region) {
		
		for(i=0;i<this.regions.length;i++) {		
			if(this.regions[i] == region) {			
				return i;			
			}			
		}		
		return -1;
	}
	
	
	// Sets the text of the status line. This method can be used if the status line text
	// is dynamic dependent on current state 
	this.setStatusLineText = function(messageText) {
		/*var txtNode = $(this.guiStatusField).children("p:first");
		if(txtNode != null)
			$(txtNode).text(messageText);*/		
	}
	
	this.setMapHeaderText = function(messageText) {
		$(this.guiMapHeadSingleText).text(messageText);
	}
	
	this.deselectAllRegions = function() {
		for(i = 0; i < this.regions.length; i++) {
			this.guiList.unselectEntry(i);
			this.guiMapContainer.hideRegion(i);
			this.guiList.enableEntry(i);
		}
			
		this.selectMode = this.C_SRC;			
		this.selectedSrcRegion = null;
		this.selectedTrgRegion = null;
			
		this.setStatusLineText(this.msg.MSG_SRC_REGION);
		// set map header
		this.setMapHeaderText(this.msg.MSG_WORLD_MAP_DEFAULT);
		$(this.guiBtnResetSelection).hide();
	}
	
	// Forwards to a service map with the specified direction
	this.dispatchServiceMap = function(serviceMap, direction) {	
		var dir = "";
		var service = "none";
		
		if(direction == this.C_WEST) {			
			dir += "west";		
		}	
		else {		
			dir += "east";
		}	
		document.location = serviceMap.url + '#' + service + '_' + dir;	
	}
	
	/*
	 * Sets all dynamic constants for the application.
	 */
	this.setMessages = function(msgObject){	
		this.msg = msgObject;
	}
}

/*
 * Controller used for servicemap template.
 * 
 */
function ControllerServiceMap() {
	
	// constants for application
	this.C_APP_PROD = 0;						// production mode
	this.C_APP_SMART_EDIT = 1;					// reddot smart edit mode
	
	// constants for direction	
	this.C_WEST = 0;							// west
	this.C_EAST = 1;							// east
	
	// constants for url parameters
	this.C_PARAM_DIRECTION = "dir";				// parameter name for service direction
	this.C_PARAM_SERVICE = "service";			// parameter name for service
	this.C_PARAM_VAL_DIRECTION_EAST = "east";	// parameter values for east/west
	this.C_PARAM_VAL_DIRECTION_WEST = "west";
	
	this.C_SMART_EDIT_PARAM_DIRECTION = "dir";		// RedDot: parameter name for service direction
	this.C_SMART_EDIT_PARAM_SERVICE = "service";	// RedDot: parameter name for service

	this.C_ANIM_SERVICE_CLICK_FADE_OUT_IN = 300;	// animation time for service change
	this.C_ANIM_SERVICE_DIRECTION_CHANGE = 600;		// animation time for direction change
	
	// model
	this.currentServiceMap = null;
	this.services = null;							// service array
	this.selectedService = null;					// currently selected service
	this.lastSelectedServiceIndex = 0;				// index of last selected service
	
	this.direction = this.C_WEST;					// current direction
	this.disabledServices = new Array();			// boolean array: disabled services for current direction
	this.urlWorldMap = null;
	this.urlSchedule = "http://fisworkweb.ad.hl.lan/en/schedules/interactive.html?view=V7060";
	
	this.msg = new Messages();						// dynamic messages	
	
	this.appMode = this.C_APP_PROD;					// current application mode (production/reddot)

	// Gui components
	this.guiStatusField = "div#grfStatusLine";		// Status Line
	this.guiMapContainer = new GuiMapContainer("div#grfMapContainer", null);
	this.guiList = new GuiList("ul#grfNavi");	
			
	// Buttons
	this.guiBtnDirection = "input#btnDirection";
	this.guiBtnResetSelection = "input#btnResetSelection";
	this.guiBtnSchedule = "input#btnSchedule";
	this.guiScheduleBox = "div#schedule";
	
	// Map Header
	this.guiMapHeaderText = "h1#mapHeaderText";
	this.guiMapHeader = "div#grfMapHeader";
	this.guiMapHeadSingleText = "h2#mapSingleText";
		
	// Transit Table
	this.guiTransitTableContainer = "div#grfTransitTables";
	this.guiTableHeader = "div#grfTableHeader";
	this.guiTable = new GuiTable("table#transit");		// Transit table
	this.guiTableHeadArrowWest = "div#tableArrowWest";
	this.guiTableHeadArrowEast = "div#tableArrowEast";
	this.guiTableHeadRegionFrom = "div#tableRegionFrom";
	this.guiTableHeadRegionTo = "div#tableRegionTo";
	
	this.guiTableInfo = "div#tableInfo";
	this.guiTableInfoComment = "div#tableInfoComment";
	this.guiTableInfoUpdate = "div#tableInfoUpdate";
	this.guiServiceInfo = "div#serviceInfo";

	
	/*
	 * initializes the controller. 
	 * 
	 * serviceMap	: Bean that contains the service map data
	 * direction	: default direction used if no url parameter are specified.
	 * smartEdit	: true -> start in RedDot smartEdit mode.
	 * htmlBaseMap	: null. If baseMap is placed in html template: html id of the image.
	 * 		->is not used here since base image is loaded and animated with JavaScript.
	 */
	this.init = function(serviceMap, direction, smartEdit, htmlBaseMap) {
		
		this.enableSmartEditMode(smartEdit);
		
		// default direction
		if(direction == this.C_WEST || direction == this.C_EAST) {
			this.direction = direction;
		}
		
		var paramDirection = null;
			
		// extract URL parameters 
		var paramService = this.getURLParam(this.C_PARAM_SERVICE);
		paramDirection = this.getURLParam(this.C_PARAM_DIRECTION);
		
		// extract URL hash used for browser back from interactive schedule
		var hashVal = this.getURLHash();
		
		// split hash parameter to retrieve service and direction information
		if(hashVal != null) {
			var params = hashVal.substring(1).split("_");
			paramService = params[0];
			paramDirection = params[1];
		}
		
		// Smart Edit in RedDot: read Cookie
		if(this.appMode == this.C_APP_SMART_EDIT) {		
			paramService = this.readCookie(this.C_SMART_EDIT_PARAM_SERVICE);
			paramDirection = this.readCookie(this.C_SMART_EDIT_PARAM_DIRECTION);		
		}
		
		// set data	
		this.currentServiceMap = serviceMap;
		this.services = serviceMap.services;
		if(this.services == null) {
			this.services = new Array();
		}
		this.disabledServices = new Array(this.services.length);	
		this.selectedService = null;
		
		// init mapContainer		
		this.guiMapContainer.setEventListener(this);
		this.guiMapContainer.init();
	
		if(paramDirection == this.C_PARAM_VAL_DIRECTION_EAST) {
			this.direction = this.C_EAST;
			this.guiMapContainer.setServiceMap(serviceMap, this.guiMapContainer.C_EASTBOUND, htmlBaseMap);
		}
		else if(paramDirection == this.C_PARAM_VAL_DIRECTION_WEST) {
			this.direction = this.C_WEST;
			this.guiMapContainer.setServiceMap(serviceMap, this.guiMapContainer.C_WESTBOUND, htmlBaseMap);
		}
		// use default direction
		else {
			if(this.direction == this.C_WEST)
				this.guiMapContainer.setServiceMap(serviceMap, this.guiMapContainer.C_WESTBOUND, htmlBaseMap);
			else
				this.guiMapContainer.setServiceMap(serviceMap, this.guiMapContainer.C_EASTBOUND, htmlBaseMap);	
		}
	
		
		// determine service if specified in url
		var index = -1;
		if(paramService != null) {		
			
			if(this.appMode == this.C_APP_SMART_EDIT) {				
				// find selected service per index
				if(paramService >=0 && paramService <this.services.length) {
						this.selectedService = this.services[paramService];
						index = paramService;	
				}		
			}	
			else {	
				// find selected service per fis code
				for(i=0;i<this.services.length;i++) {				
					if(paramService.toString().toLowerCase() == this.services[i].fisCode.toString().toLowerCase()) {
						this.selectedService = this.services[i];
						index = i;	
					}
				}
			}	
		}
				
		// initialize service list
		this.guiList.setEventListener(this);
		this.guiList.setEntries(this.services);
		
		this.setStatusLineText(this.msg.MSG_SERVICE_MAP_NO_SELECTION);
		// set map header
		this.setMapHeaderText("");	
		this.setScheduleBoxMessage(this.msg.MSG_SERVICE_MAP_SCHEDULE_SELECTION_REGION);
		$(this.guiBtnSchedule).attr("value", this.msg.MSG_SERVICE_MAP_BTN_SCHEDULE_REGION);

		// initialize transit table	
		$(this.guiTransitTableContainer).hide();
		this.guiTable.init(0,0);
	
		// init buttons
		$(this.guiBtnResetSelection).css("visibility","hidden");
		$(this.guiBtnResetSelection).attr("value",this.msg.MSG_SERVICE_MAP_BTN_RESET_SELECTION);
		
		// show service map, list and show respective direction
		this.guiMapContainer.showServiceMap();
		this.guiList.show();
		this.showDirectionStatus(this.direction, serviceMap);
		
	
		// service is specified in url
		if(this.selectedService != null) {
			
			// specified service is enabled
			if(!this.disabledServices[index]) {	
				this.setScheduleBoxMessage(this.msg.MSG_SERVICE_MAP_SCHEDULE_SELECTION_SERVICE);
				$(this.guiBtnSchedule).attr("value", this.msg.MSG_SERVICE_MAP_BTN_SCHEDULE_SERVICE);
				this.guiList.selectEntry(index);
				
				// set map header
				this.setMapHeaderText(this.services[index].longName + " (" + this.services[index].name + ")");
				
				this.guiMapContainer.showService(index);
				this.setTransitTable(index);
				$(this.guiBtnResetSelection).css("visibility","visible");	
				$(this.guiTransitTableContainer).fadeIn(80);
				
				if(this.appMode == this.C_APP_SMART_EDIT) {				
					var elem = "div#smartedit_" + index;
					$(elem).show();
				}				
			}
			// specified service is disabled
			else {
				// smart edit mode: switch to other direction
				if(this.appMode == this.C_APP_SMART_EDIT) {			
					this.setScheduleBoxMessage(this.msg.MSG_SERVICE_MAP_SCHEDULE_SELECTION_SERVICE);
					$(this.guiBtnSchedule).attr("value", this.msg.MSG_SERVICE_MAP_BTN_SCHEDULE_SERVICE);
					this.guiList.selectEntry(index);
					this.handleDirectionChangeClick();
					//this.setTransitTable(index);
					$(this.guiBtnResetSelection).css("visibility","visible");	
					$(this.guiTransitTableContainer).fadeIn(80);
					
					var elem = "div#smartedit_" + index;
					$(elem).show();	
				}
				else {
					this.selectedService = null;
				}
			}
		}
	}
	
	this.setSmartEditParam = function(serviceCookieName) {
		this.C_SMART_EDIT_PARAM_SERVICE = serviceCookieName;
	}
	
	/*
	 * Extracts the specified parameter from the current url string
	 */
	this.getURLParam = function(strParamName){
	  
	  var strReturn = "";
	  var strHref = window.location.href;
	  var bFound=false;
	  
	  var cmpstring = strParamName + "=";
	  var cmplen = cmpstring.length;

	  if ( strHref.indexOf("?") > -1 ){
	    var strQueryString = strHref.substr(strHref.indexOf("?")+1);
	    var aQueryString = strQueryString.split("&");
	    for ( var iParam = 0; iParam < aQueryString.length; iParam++ ){
	      if (aQueryString[iParam].substr(0,cmplen)==cmpstring){
	        var aParam = aQueryString[iParam].split("=");
	        strReturn = aParam[1];
	        bFound=true;
	        break;
	      }
	      
	    }
	  }
		if (bFound==false) 
			return null;
		
		return strReturn;
	}
	
	/*
	 * returns url hash parameter.
	 */
	this.getURLHash = function() {
		var hash = document.location.hash;
		
		if(hash != null && hash != undefined && hash != "")
			return hash;
		else
			return null;
	}
	
	
	// Sets the table and map header if the service direction is changed by the user 
	this.showDirectionStatus = function(direction, serviceMap){
		
		if(direction == this.C_WEST) {			
					
			if(serviceMap.regionFrom != null && serviceMap.regionTo != null ) {
				// set map header
				$(this.guiMapHeaderText).text(this.msg.MSG_SERVICE_MAP_HEADER_MAP_SERVICES_FROM + serviceMap.regionTo.name + this.msg.MSG_SERVICE_MAP_HEADER_MAP_MIDDLE + serviceMap.regionFrom.name);	  
			}
	
			// set table header	
			$(this.guiTableHeadRegionFrom).text(serviceMap.regionFrom.name);
			$(this.guiTableHeadRegionTo).text(serviceMap.regionTo.name);
			$(this.guiTableHeadArrowWest).show();
			$(this.guiTableHeadArrowEast).hide();
					
			// Change button description		
			$(this.guiBtnDirection).attr("value", this.msg.MSG_SERVICE_MAP_BTN_DIRECTION_WEST);
					
			// disable all services that do not offer a route for this direction		 
			for(i = 0; i < this.services.length; i++) {		
				this.disabledServices[i] = false; 
				
				if(this.services[i].westRoute == null) {			
					this.disabledServices[i] = true;
					this.guiList.disableEntry(i);								
				}
				else {
					this.guiList.enableEntry(i);	
				}
			}
		
		}
		else if (direction == this.C_EAST) {
		
			// set map header			
			if(serviceMap.regionFrom != null && serviceMap.regionTo != null ) {
				// set map header
				$(this.guiMapHeaderText).text(this.msg.MSG_SERVICE_MAP_HEADER_MAP_SERVICES_FROM + serviceMap.regionFrom.name + this.msg.MSG_SERVICE_MAP_HEADER_MAP_MIDDLE + serviceMap.regionTo.name);
			}
		
			// set table header	
			$(this.guiTableHeadRegionFrom).text(serviceMap.regionFrom.name);
			$(this.guiTableHeadRegionTo).text(serviceMap.regionTo.name);
			$(this.guiTableHeadArrowWest).hide();
			$(this.guiTableHeadArrowEast).show();
			
			$(this.guiBtnDirection).attr("value", this.msg.MSG_SERVICE_MAP_BTN_DIRECTION_EAST);
			
			// disable all services that do not offer a route for this direction	
			for(i = 0; i < this.services.length; i++) {			
				this.disabledServices[i] = false; 
				
				if(this.services[i].eastRoute == null) {
					this.disabledServices[i] = true;
					this.guiList.disableEntry(i);								
				}
				else {
					this.guiList.enableEntry(i);	
				}			
			}		
		}	
	}
	
	/*
	 * returns array index of specified service object.
	 */
	this.getServiceIndex = function(service) {
		
		for(i=0;i<this.services.length;i++) {		
			if(this.services[i] == service) {			
				return i;			
			}			
		}		
		return -1;
	}
	
	// event listener functions triggered by other GUI components (list and mapContainer)
	
	// Connection to event handler
	this.evtGuiListEntryHoverIn = function(index, object) {		
		this.handleServiceHoverIn(index, this.services[index]);	
	}
	
	// Connection to event handler
	this.evtGuiListEntryHoverOut = function(index, object) {	
		this.handleServiceHoverOut(index, this.services[index]);			
	}
	
	// Connection to event handler
	this.evtGuiListEntryClick = function(event, index, object) {
		this.handleServiceClick(event, index, this.services[index]);	
	}
		
	this.evtGuiListHoverIn = function() {	
	}
	
	this.evtGuiListHoverOut = function() {
			
		// no service is selected --> hide service information
		if(this.selectedService == null) {
			this.guiList.unhighlightEntry(this.lastSelectedServiceIndex);	
			
			// set map header
			this.setMapHeaderText("");
				
			this.guiMapContainer.hideService(this.lastSelectedServiceIndex);
			$(this.guiTransitTableContainer).hide();
			$(this.guiServiceInfo).hide();
		}
			
		// service is selected --> show selected service and transit table	
		else if(this.selectedService != null) {
			
			var index = this.getServiceIndex(this.selectedService);
			
			// if last hovered service is different from selected one -> show selected one
			if(index != this.lastSelectedServiceIndex) {
				this.guiList.unhighlightEntry(this.lastSelectedServiceIndex);		
				this.guiMapContainer.hideService(this.lastSelectedServiceIndex);	
				
				// set map header
				this.setMapHeaderText(this.services[index].longName + " (" + this.services[index].name + ")");
					
				this.guiMapContainer.showService(index);
				this.setTransitTable(index);
			}	
			
			this.lastSelectedServiceIndex = index;
		}	
	}
	
	// btnResetSelection onclick handler
	this.evtGuiBtnResetSelectionClick = function(){
		this.deselectAllServices();
	}
	
	this.evtGuiBtnBackToWorldmapClick = function(){		
		if(this.urlWorldMap != null) {			
			this.setURL(this.urlWorldMap);
		}	
	}
	
	this.evtGuiBtnDirectionChangeClick = function(){	
		this.handleDirectionChangeClick();	
	}

	// Connection to event handler
	this.evtGuiMapContainerHoverIn = function(index, srcType, object) {	
		if (srcType == this.guiMapContainer.C_SERVICE) {
		}	
	}
	
	// Connection to event handler: Captures hover events within the map
	this.evtGuiMapContainerHoverOut = function(index, srcType, object) {		
		if (srcType == this.guiMapContainer.C_SERVICE) {		
		}	
	}
	
	this.evtGuiMapContainerClick = function(event, index, srcType, object) {
		// User clicks on a service
		if (srcType == this.guiMapContainer.C_SERVICE ||
			srcType == this.guiMapContainer.C_BASE_MAP) {		
			//this.deselectAllServices();						
		}
	}
	
	// Click-handler for the OnlineSchedule Button.  	
	this.handleOnlineScheduleClick = function() {
		
		var url = null;
			
		// link to schedule with preselected source and target region
		if(this.selectedService == null && this.currentServiceMap.regionFrom != null &&
											this.currentServiceMap.regionTo != null) {
			
			var urlFilter = "&filter=limitPortsByRegion";
			var urlLoadRegion = "&loadRegion=";
			var urlDischargeRegion = "&dischargeRegion=";
		
			if(this.guiMapContainer.serviceMode == this.guiMapContainer.C_WESTBOUND) {
				
				url = this.urlSchedule + urlFilter + urlLoadRegion + 
					  this.currentServiceMap.regionTo.fisCode + urlDischargeRegion + 
					  this.currentServiceMap.regionFrom.fisCode;
				
			}
			// eastbound
			else {			
				url = this.urlSchedule + urlFilter + urlLoadRegion + 
					  this.currentServiceMap.regionFrom.fisCode + urlDischargeRegion + 
					  this.currentServiceMap.regionTo.fisCode;	
			}

			this.setURL(url);		
		} 
		// link to schedule with preselected service
		else {
						
			if(this.selectedService.fisCode != null ) {
			
				var urlFilter = "&filter=limitPortsByService";
				var urlService = "&service=";
				var urlDirection = "&direction=";
				
				var west = "w";
				var east = "e";
				
				if(this.selectedService.reverseDirectionCode) {			
					west = "e";
					east = "w";			
				}
			
				if(this.guiMapContainer.serviceMode == this.guiMapContainer.C_WESTBOUND) {		
					url = this.urlSchedule + urlFilter + urlService + this.selectedService.fisCode
										+ urlDirection + west;								
	
				}
				else {			
					url = this.urlSchedule + urlFilter + urlService + this.selectedService.fisCode
										+ urlDirection + east;
			
				}
				
				// set hash for browser back
				this.setURLHashParams();
				
				// link to interactive schedule
				this.setURL(url);
			}
			// fisCode does not exist --> no link to schedule
			else {			
				alert(this.msg.MSG_SERVICE_MAP_NO_SCHEDULE_LINK);			
			}
		}	
	}
	

	// Moves to the specified URL
	this.setURL = function(strURL){
		document.location = strURL;
	}
	
	// Moves to the specified URL without history entry
	this.replaceURL = function(strURL){
		location.replace(strURL);
	}
	
	/*
	 * days = 0: Cookie is trashed when browser is closed
	 * days < 0: Cookie is trashed immediately
	 */
	this.setCookie = function(name, value, days) {
		
		if(days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
		//ppkcookie1=testcookie; expires=Thu, 2 Aug 2001 20:47:11 UTC; path=/'
	}
	
	// helper method to store service and direction as hash in the URL
	this.setURLHashParams = function(){
		
		var fisCode = "none";
		if(this.selectedService != null) {		
			if(this.selectedService.fisCode != null && this.selectedService.fisCode != "") {
				fisCode = this.selectedService.fisCode;
			}	
		}
		
		
		var hashVal = "#" + fisCode + "_";				
		
		if(this.direction == this.C_WEST)
			hashVal+=this.C_PARAM_VAL_DIRECTION_WEST;
		else
			hashVal+=this.C_PARAM_VAL_DIRECTION_EAST;
					
		this.replaceURL(hashVal);	
	} 
	
	this.readCookie = function(name) {
		
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') 
				c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) 
			return c.substring(nameEQ.length,c.length);
		}
		
		return null;
	}
	
	/*
	 * Sets smartEdit mode.
	 * 
	 * enabled: true/false.
	 */
	this.enableSmartEditMode = function(enabled) {
		
		if(enabled == true) {
			this.appMode = this.C_APP_SMART_EDIT;
		}
		else if(enabled == false) {
			this.appMode = this.C_APP_PROD;
			
			// erase cookie
			this.setCookie(this.C_PARAM_SERVICE,null,-1);
		}

	}  

	/*
	 * not used anymore. Link is static within the html template.
	 */
	this.setWorldMapURL = function(urlWorldMap) {	
		this.urlWorldMap = urlWorldMap;
	}
	
	/*
	 * Sets the base url to the interactive schedule in the online business.
	 * 
	 */
	this.setScheduleURL = function(scheduleURL){
		this.urlSchedule = scheduleURL;
	}
	
	/*
	 * Sets all dynamic constants for the application.
	 */
	this.setMessages = function(msgObject){	
		this.msg = msgObject;
	}
	
	
	// Sets the text of the status line below the page title. 
	// This method can be used if the status line text is dynamic dependent on current state. 
	// currently not used since text line is static.
	this.setStatusLineText = function(messageText) {
		/*var txtNode = $(this.guiStatusField).children("p:first");
		if(txtNode != null)
			$(txtNode).text(messageText);*/		
	}
	
	// Sets the map header text, which is dynamic dependent on current direction
	this.setMapHeaderText = function(messageText) {
		$(this.guiMapHeadSingleText).text(messageText);
	}
	
	// Sets the text of the schedule link box
	this.setScheduleBoxMessage = function(messageText) {	
		var txtNode = $(this.guiScheduleBox).children("p:first");
		if(txtNode != null)
			$(txtNode).text(messageText);		
	}
	
	
	//Sets the table that shows the transit times for the service specified by 'index'.
	this.setTransitTable = function(index) {
		
		var route = null;
		
		// extract westbound transit times
		if(this.direction == this.C_WEST){
		 	route = this.services[index].westRoute;
		}
		// eastbound transit times
		else {				
			 route = this.services[index].eastRoute;
		}
		
		if(route != null && route.srcRegionPorts != null && route.trgRegionPorts != null 
			&& route.transitTable != null) {
			
			// update table	
			this.guiTable.fillCrossTable(this.msg.MSG_SERVICE_MAP_TABLE_FROM_TO, 
								route.srcRegionPorts, route.trgRegionPorts, 
								route.transitTable);
		}
		else {
			this.guiTable.reset(0,1);
			this.guiTable.setHeader(0,0,this.msg.MSG_SERVICE_MAP_TABLE_FROM_TO);
		}	
		
		// update table comment and service info:	
		this.setTableInfo(index,this.direction);
		this.setServiceInfo(index);		
	}
	
	/*
	 * Sets the comment line at the bottom of the transit table, and the 
	 * update time of the table.
	 * 
	 * Values are copied from hidden div containers.
	 */	
	this.setTableInfo = function(index, direction) {
		
		var data = $(this.createTableInfoID(index, direction)).children("div");
			
		var textComment = $(data[0]).children("p:eq(0)").text();
		var textUpdate =  $(data[1]).children(":eq(1)").children("p:eq(0)").text();
				
		var fields = $(this.guiTableInfo).children("div");
		
		$(fields[0]).children("p:eq(0)").text(textComment);
		$(fields[1]).children(":eq(1)").children("p:eq(0)").text(textUpdate);		
	}
	
	/*
	 * Sets the service information that apppears between map and transit table 
	 * for a specified service.
	 *  
	 * Value is copied from hidden div container. Value has to be wrapped into a <p> element
	 * placed within the div container.
	 */
	this.setServiceInfo = function(index) {	
		var text = $(this.createServiceInfoID(index)).children("p:eq(0)").text();	
		$(this.guiServiceInfo).children("p:eq(0)").text(text);
	}
	
	/*
	 * Creates the html id of the hidden html container that contains the table comments.
	 */
	this.createTableInfoID = function(index, direction) {
		
		var tableInfo = "div#tableInfo_" + index + "_";
		if(direction == this.C_WEST)
			tableInfo = tableInfo + "west";
		else
			tableInfo = tableInfo + "east";
		
		return tableInfo;
	}
	
	/*
	 * Creates the html id of the hidden html container that contains the service info.
	 */
	this.createServiceInfoID = function(index) {	
		var serviceInfo = "div#serviceInfo_" + index;	
		return serviceInfo;
	}
	
	// user changes service direction (eastbound <--> westbound)
	this.handleDirectionChangeClick = function() {
		
		// toggle to eastbound direction
		if(this.guiMapContainer.serviceMode == this.guiMapContainer.C_WESTBOUND) {		
			this.guiMapContainer.setDirection(this.guiMapContainer.C_EASTBOUND);
			this.direction = this.C_EAST;
					
			var index = this.getServiceIndex(this.selectedService);
						
			if(index > -1) {
				
				// route for westbound direction exists	
				if(this.selectedService.eastRoute != null) {
						//this.guiMapContainer.showService(index);
						this.guiMapContainer.fadeInService(i,this.C_ANIM_SERVICE_DIRECTION_CHANGE);
						this.setTransitTable(index);
				}
				// no route defined
				else {
						// set map header
						this.setMapHeaderText("");
						
						this.guiMapContainer.hideService(index);
						this.selectedService = null;
						this.guiTable.init(0,0);	
					}		
			}
			// no service was selected
			else {
			}
							
			this.showDirectionStatus(this.C_EAST, this.guiMapContainer.serviceMap);	
			
			// RedDot maintenance: shows edit_table / set cookie
			if(this.appMode == this.C_APP_SMART_EDIT) {					
					this.setCookie(this.C_SMART_EDIT_PARAM_DIRECTION, this.C_PARAM_VAL_DIRECTION_EAST,0);			
			}
			
			this.setURLHashParams();
		}
		// toggle to westbound direction
		else {
			this.guiMapContainer.setDirection(this.guiMapContainer.C_WESTBOUND);		
			this.direction = this.C_WEST;
			
			// show westbound route of selected service
			var index = this.getServiceIndex(this.selectedService);
			
			if(index > -1) {
				
				// route for other direction exists	
				if(this.selectedService.westRoute != null) {
						//this.guiMapContainer.showService(index);
						this.guiMapContainer.fadeInService(i,this.C_ANIM_SERVICE_DIRECTION_CHANGE);
						this.setTransitTable(index);
				}
				// no route defined
				else {
						// set map header
						this.setMapHeaderText("");
						
						this.guiMapContainer.hideService(index);
						this.selectedService = null;
						this.guiTable.init(0,0);	
					}		
			}
			// no service was selected
			else {				
			}
			
			this.showDirectionStatus(this.C_WEST, this.guiMapContainer.serviceMap);
			
			// RedDot maintenance: shows edit_table / set cookie
			if(this.appMode == this.C_APP_SMART_EDIT) {					
				this.setCookie(this.C_SMART_EDIT_PARAM_DIRECTION, this.C_PARAM_VAL_DIRECTION_WEST,0);			
			}	
			
			this.setURLHashParams();
		}
	}
	
	
	this.handleServiceHoverIn = function(index, service) {
		
		// no service is selected
		if(this.selectedService == null) {
		
			// unhighlight previously hovered service
			this.guiList.unhighlightEntry(this.lastSelectedServiceIndex);		
			this.guiMapContainer.hideService(this.lastSelectedServiceIndex);
						
			// service is enabled
			if(!this.disabledServices[index]) {

				$(this.guiTransitTableContainer).show();
				$(this.guiServiceInfo).show();
		
				// highlight service in list, on map and update transit times
				this.guiList.highlightEntry(index);
				
				// set map header
				this.setMapHeaderText(service.longName + " (" + service.name + ")");
				
				this.guiMapContainer.showService(index);				
				this.setTransitTable(index);
			}
			// service is disabled
			else {
				// set map header
				this.setMapHeaderText("");
					
				$(this.guiTransitTableContainer).hide();
				$(this.guiServiceInfo).hide();			
			}
			
			this.lastSelectedServiceIndex = index;		
		}
		// service is selected
		else {
			
			if(index != this.lastSelectedServiceIndex) {
				this.guiList.unhighlightEntry(this.lastSelectedServiceIndex);	
			}
			
			// hovered service is enabled -> show service
			if (!this.disabledServices[index]) {
				
				// hide previous service
				if(index != this.lastSelectedServiceIndex) {	
					this.guiMapContainer.hideService(this.lastSelectedServiceIndex);
				}
					
				if(this.selectedService != service) {
					this.guiList.highlightEntry(index);
				}
				
				// set map header
				this.setMapHeaderText(service.longName + " (" + service.name + ")");
				
				this.guiMapContainer.showService(index);
				this.lastSelectedServiceIndex = index;
				this.setTransitTable(index);	
			}
		
			// hovered service is disabled --> show selected service
			else if (this.disabledServices[index]) {
				
				var selectedServiceIndex = this.getServiceIndex(this.selectedService);
				
				if(index != this.lastSelectedServiceIndex && 
					this.lastSelectedServiceIndex != selectedServiceIndex) {	
					
					this.guiMapContainer.hideService(this.lastSelectedServiceIndex);
					
					// set map header
					this.setMapHeaderText(this.selectedService.longName + " (" + this.selectedService.name + ")");
					
					this.guiMapContainer.showService(selectedServiceIndex);		
				}
								
				this.lastSelectedServiceIndex = selectedServiceIndex;					
				this.setTransitTable(selectedServiceIndex);
			}
		}
		
	}
	
	/*
	 * not used here
	 */
	this.handleServiceHoverOut = function(index, service) {
		
	}
	
	this.handleServiceClick = function(event, index, service) {
		
		if(!this.disabledServices[index]) {
			
			// user again clicks on the service
			if(this.selectedService == service) {	
			
			}
			// user has selected another service
			else {
				this.selectedService = service;
			
				// unselect previously selected service
				for(t = 0; t < this.services.length; t++) {
					
					if(t!=index && !this.disabledServices[t]) {
						this.guiList.unselectEntry(t);
						this.guiMapContainer.hideService(t);
							
						if(this.appMode == this.C_APP_SMART_EDIT) {
					
							var elem = "div#smartedit_" + t;
							$(elem).hide();
	
						}			
					}
				}
				
				// eastbound or westbound route are not defined --> disable button
				if((this.direction == this.C_WEST && service.eastRoute == null) ||
				   (this.direction == this.C_EAST && service.westRoute == null)) {
					$(this.guiBtnDirection).hide();
				}
				// both directions exist --> enable button
				else {
					$(this.guiBtnDirection).show();
				}
				
				// highlight currently selected service
				this.guiList.selectEntry(index);
				this.setScheduleBoxMessage(this.msg.MSG_SERVICE_MAP_SCHEDULE_SELECTION_SERVICE);
				$(this.guiBtnSchedule).attr("value", this.MSG_SERVICE_MAP_BTN_SCHEDULE_SERVICE);
				
				//this.guiMapContainer.fadeOutService(index, this.C_ANIM_SERVICE_CLICK_FADE_OUT_IN);
				//this.guiMapContainer.hideService(index);
				this.guiMapContainer.fadeInService(index, this.C_ANIM_SERVICE_CLICK_FADE_OUT_IN);
				
				
				
				
				$(this.guiBtnSchedule).attr("value", this.msg.MSG_SERVICE_MAP_BTN_SCHEDULE_SERVICE);
				
				// set map header
				this.setMapHeaderText(this.selectedService.longName + " (" + this.selectedService.name + ")");
						
				this.setTransitTable(index);
				$(this.guiBtnResetSelection).css("visibility", "visible");	
				this.setStatusLineText(this.msg.MSG_SERVICE_MAP_SERVICE_SELECTION);
								
				// RedDot maintenance: shows edit_table / set cookie
				if(this.appMode == this.C_APP_SMART_EDIT) {							
					this.setCookie(this.C_SMART_EDIT_PARAM_SERVICE, index,0);			
					var elem = "div#smartedit_" + index;
					$(elem).show();
				}
				
				this.setURLHashParams();
			}
		}
	}
	
	/*
	 * unselects the currently selected service.
	 */
	this.unselectService = function() {
		
		this.selectedService = null;
		this.setStatusLineText(this.msg.MSG_SERVICE_MAP_NO_SELECTION);
		// set map header	
		this.setMapHeaderText("");			
				
		for(t = 0; t < this.services.length; t++) {				
			if(!this.disabledServices[t])
				this.guiList.unselectEntry(t);
			else
				this.guiList.disableEntry(t);
		
			this.guiMapContainer.hideService(t);	
		}
		
		$(this.guiBtnResetSelection).hide();			
		this.setScheduleBoxMessage(this.msg.MSG_SERVICE_MAP_SCHEDULE_SELECTION_REGION);
		$(this.guiBtnSchedule).attr("value", this.msg.MSG_SERVICE_MAP_BTN_SCHEDULE_REGION);
		$(this.guiBtnDirection).show();		
	}
	
	this.deselectAllServices = function() {
		
		this.unselectService();		
		$(this.guiServiceInfo).hide();	
		$(this.guiTransitTableContainer).hide();
		this.guiTable.init(0,0);
		
		var dir = "west";
		if(this.direction == this.C_WEST) {
			dir = "west";
		}
		else if(this.direction == this.C_EAST) {
			dir = "east";	
		}	
		this.replaceURL("#none" + '_' + dir);
	}
}

/**
 * MapContainer.
 * Contains the maps and map items such as labels, ports, ...
 * 
 * Reference point for the positioning of map items is the top left corner of the 
 * html div container.
 * 
 * id: hmtl object id
 */
function GuiMapContainer(htmlID, worldmap) {

	// Constants
	this.C_REGION = 0;
	this.C_SERVICE = 1;
	
	this.C_BASE_MAP = 5;
	
	this.C_WORLD_MAP = 0;
	this.C_SERVICE_MAP = 1;
	
	this.C_WESTBOUND = 0;			// constants for direction
	this.C_EASTBOUND = 1;

	this.id = htmlID;					// html div container
	
	this.rp = new Point2D(0,0);			// reference point top left corner (0,0) for map items	
	this.eventListener = null;
	
	this.width = 0;
	this.height = 0;
	
	// data		
	this.worldmap = null;	
	this.regions = null;
	this.services = new Array();
	this.serviceMap = null;
	
	this.ajaxLoader = null;
	
	// state
	this.viewMode = this.C_WORLD_MAP;			// service view or world view
	this.serviceMode = this.C_WESTBOUND; 		// direction
	
	
	// html objects (ids)
	this.refWorldMap = null;					// id of world base map image
	this.refServiceMap = null;					// id of service base map image
	
	this.refRegions = new Array();				// ids of rendered images
	this.refServices = new Array();				// ids of rendered images
	
	this.refAjaxLoader = null;
	
			
	// private method:
	
	this.createMapImgItem = function(idName, mapImage) {
		
		var imgID = "img#" + idName;	
		
		//alert("z" + id);
		if(mapImage != null) {
			$(this.id).append("<img id='" + idName + "' class='grfMapItem' src='" + mapImage.img.src + "' GALLERYIMG='no'> </img> ");
		}
			
		else
			$(this.id).append("<img id='" + idName + "' class='grfMapItem' src='' GALLERYIMG='no'> </img> ");
	
			
		$(imgID).hide();
		// position map image relative to base image	
		if(mapImage != null)	
			this.placeFeature(imgID, mapImage.x, mapImage.y);
		else 
			this.placeFeature(imgID, 0, 0);
				
		return imgID;
		
	}
	
	// private method:	
	// pre: object with specified ID exists
	this.updateMapImgItem = function(itemID, mapImage) {
		
		// set new img source
		if(mapImage != null) {
			$(itemID).attr("src", $(mapImage.img).attr('src'));
		}
		else
			$(itemID).attr("src","");
			
		$(itemID).hide();
		
		if(mapImage != null)	
			this.placeFeature(itemID, mapImage.x, mapImage.y);
	}
	

	/*
	 * Should be called after object instantiation
	 * 
	 */		
	this.init = function() {	
		// calc map reference point
		this.calcMapRefPoint();		
		this.calcMapSize();
	}
	
	this.calcMapSize = function() {		
		this.width = $(this.id).width();
		this.height = $(this.id).height();		
	}
	
	/*
	 * not used. We use css properties to show the ajax icon.
	 */
	this.setAjaxLoader = function(mapImage) {
		
		this.ajaxLoader = mapImage;
		
		if(this.refAjaxLoader == null) {		
			mapImage.loadImage();			
			this.refAjaxLoader = this.createMapImgItem("ajaxLoader", mapImage);
		}	
		else {			
			this.refAjaxLoader = this.updateMapImgItem("ajaxLoader", mapImage);			
		}
	}
		
	/*
	 * Initializes the specified service map.
	 * Loads all image data (service map + services)
	 * 
	 */
	this.setServiceMap = function(serviceMap, direction, htmlBaseMap) {
			
		this.serviceMap = serviceMap;
		this.services = serviceMap.services; 
		
		if(this.services == null) {
			this.services = new Array();
		}
		
		this.viewMode = this.C_SERVICE_MAP;
		
		// load serviceMap image with fadeIn	
		this.serviceMap.mapImage.loadImage(400, "img#serviceMap");
		
		
		// load service images for eastbound / westbound routes
		for(i=0;i<this.services.length;i++) {
			
			if(this.services[i].westRoute != null && this.services[i].westRoute.mapImage != null)
				this.services[i].westRoute.mapImage.loadImage();

			if(this.services[i].eastRoute != null && this.services[i].eastRoute.mapImage != null)				
				this.services[i].eastRoute.mapImage.loadImage();
		}
	
		// create service map object
		if(!htmlBaseMap || htmlBaseMap == undefined)
			this.refServiceMap = this.createMapImgItem("serviceMap", this.serviceMap.mapImage);
		else {
			this.refServiceMap = htmlBaseMap;		
		}
		
		var selfRef = this;
		$(this.refServiceMap).click(function(event){
			event.preventDefault();
			selfRef.eventListener.evtGuiMapContainerClick(event, -1, selfRef.C_BASE_MAP, null);
		});
			
		this.serviceMode = direction;
			
		// create service objects
		for(i = 0; i < this.services.length; i++) {		
			this.setService(i);
		}
	}
	
	
	this.showServiceMap = function() {		
		$(this.refServiceMap).fadeIn(400);
	}
	
	// Sets the region image with the specified index.
	this.setRegion = function(index) {
	
		if(this.regions == null) {		
			this.regions = this.worldmap.regions;
		}
		
		var i = index;
			
		// img object already exists
		if(i<this.refRegions.length) {		
			// update img object
			this.updateMapImgItem(this.refRegions[i], this.regions[i].mapImage);		
		}
		else {
			
			// create new img object		
			var itemName = "reg" + i;	
			var itemRef = this.createMapImgItem(itemName, this.regions[i].mapImage);
			
			$(itemRef).css("cursor", "hand");
			
			this.refRegions.push(itemRef);
			
			// add event listener
			var selfRef = this;

			$(itemRef).hover(function () {				
						selfRef.eventListener.evtGuiMapContainerHoverIn(index, selfRef.C_REGION, selfRef.regions[i]);
						// fire event to listener
					},
					function () { 
  				   		selfRef.eventListener.evtGuiMapContainerHoverOut(index, selfRef.C_REGION, selfRef.regions[i]);
						// fire event to listener
			});
	
			$(itemRef).click(function(event){
				event.preventDefault();
				selfRef.eventListener.evtGuiMapContainerClick(event, i, selfRef.C_REGION, selfRef.regions[i]);
			});

		}

	}
	
	this.setService = function(index) {
	
		if(this.services == null) {		
			this.services = this.serviceMap.services;
		}
		
		var i = index;
		
		// show east or westbound route
		var img = null;
		if(this.serviceMode == this.C_WESTBOUND) {
			
			if(this.services[i].westRoute != null && this.services[i].westRoute.mapImage != null)
				img = this.services[i].westRoute.mapImage;
		}
		else if (this.serviceMode == this.C_EASTBOUND){
			
			if(this.services[i].eastRoute != null && this.services[i].eastRoute.mapImage != null)
				img = this.services[i].eastRoute.mapImage;
		}
				
		// img object already exists
		if(i<this.refServices.length) {		
			// update img object
			this.updateMapImgItem(this.refServices[i], img);
		}
		else {
			
			// create new img object		
			var itemName = "svc" + i;	

			var itemRef = this.createMapImgItem(itemName, img);
			
			//$(itemRef).css("cursor", "hand");
			
			this.refServices.push(itemRef);
			
			// add event listener			
			var selfRef = this;
						
			$(itemRef).hover(function () {
						selfRef.eventListener.evtGuiMapContainerHoverIn(index, selfRef.C_SERVICE, selfRef.services[i]);
						// fire event to listener
					},
					function () { 
  				   		selfRef.eventListener.evtGuiMapContainerHoverOut(index, selfRef.C_SERVICE, selfRef.services[i]);
						// fire event to listener
			});
	
			$(itemRef).click(function(event){
				event.preventDefault();
				selfRef.eventListener.evtGuiMapContainerClick(event, i, selfRef.C_SERVICE, selfRef.services[i]);
			});
		}
	}
	
	/*
	 * Initializes the specified worldMap.
	 * 
	 * Loads the image data of the world map, regions and service map 
	 * base images.
	 * 
	 * 
	 */	
	this.setWorldMap = function(worldMap) {
		
		this.worldmap = worldMap;
		
		// load Worldmap image
		this.worldmap.mapImage.loadImage(400, "img#worldMap");
	
		// load Region images
		for(i=0; i<this.worldmap.regions.length;i++) {	
			if(this.worldmap.regions[i].mapImage != null) {
				this.worldmap.regions[i].mapImage.loadImage();
			}		 
		}
	
		// load serviceMap images
		for(i=0; i<this.worldmap.serviceMaps.length;i++) {
			if(this.worldmap.serviceMaps[i].mapImage != null)
				this.worldmap.serviceMaps[i].mapImage.loadImage();
		}
			
		// create worldmap object	
		this.refWorldMap = this.createMapImgItem("worldMap", this.worldmap.mapImage);
		
		var selfRef = this;
		$(this.refWorldMap).click(function(event){
								event.preventDefault();
								selfRef.eventListener.evtGuiMapContainerClick(event, -1, selfRef.C_BASE_MAP, null);
		});
	
		// create regions
		this.regions = this.worldmap.regions;
		for(i=0;i<regions.length;i++) {	
			this.setRegion(i);				
		}	
	}

	/*
	 * Changes the direction to eastbound/westbound
	 * 
	 * direction: C_EASTBOUND = 1  C_WESTBOUND = 0
	 * 
	 */
	this.setDirection = function(direction) {
		
		this.serviceMode = direction;		
	
		// change all services to new direction
		if(this.services != null) {		
			for(i=0;i<this.services.length;i++) {		
				this.setService(i);	
			}
			
			for(i=0;i<this.services.length;i++) {	
				this.hideService(i);
			}			
		}		
	}
	
	/*
	 * not used
	 */
	this.showAjaxLoader = function() {
		if(this.refAjaxLoader != null)
			$(this.refAjaxLoader).show();
	}
	
	/*
	 * not used
	 */
	this.hideAjaxLoader = function() {
		if(this.refAjaxLoader != null)
			$(this.refAjaxLoader).hide();
	}
	
	
	this.showService = function(index) {
		var o = this.refServices[index];
		//$(o).show();
		$(o).fadeIn(80);
	}
	
	this.hideService = function(index) {
		var o = this.refServices[index];
		$(o).hide();
	}
	
	this.fadeInService = function(index, time) {
		var o = this.refServices[index];
		$(o).fadeIn(time);
	}
	
	this.fadeOutService = function(index, time) {
		var o = this.refServices[index];
		$(o).fadeOut(time);
	}
	
	this.showRegion = function(index) {
		var o = this.refRegions[index];
		$(o).css({ filter:'alpha(opacity=100)',opacity:'1.0'});	
		$(o).css("cursor", "hand");
	}
	
	this.showTransparentRegion = function(index, alpha) {
		var o = this.refRegions[index];	
		var ie = alpha*100;	
		$(o).css({ filter:"alpha(opacity="+ ie + ")", opacity:alpha});
	}
	
	this.hideRegion = function(index) {
		var o = this.refRegions[index];
		$(o).css({ filter:'alpha(opacity=0)',opacity:'0.0'});	
		$(o).css("cursor", "default");
	}
	
	this.disableRegion = function(index) {
		var o = this.refRegions[index];
		$(o).css({ filter:'alpha(opacity=0)',opacity:'0.0'});
		$(o).hide();
	}
	
	this.enableRegion = function(index) {
		var o = this.refRegions[index];
		$(o).css({ filter:'alpha(opacity=0)',opacity:'0.0'});
		$(o).show();
	}
	
	
	// public method:
	this.setEventListener = function(listener) {	
		this.eventListener = listener;	
	}
	
	/*
	 * Loads the worldmap if already initialized.
	 * Only used in dynamic version
	 * pre: setWorldMap
	 */
	this.loadWorldMap = function() {
		
		//$(this.refServiceMap).hide();
		
		for(i=0;i<this.services.length;i++) {			
			$(this.refServices[i]).hide();
			
		}
	
		$(this.refWorldMap).hide();
		$(this.refWorldMap).fadeIn(400);
		
		for(i=0;i<this.regions.length;i++) {		
			this.enableRegion(i);
		}
	}
	
	/*
	 * Loads the specified service map.
	 * Only used in dynamic version
	 */	
	this.loadServiceMap = function(serviceMap) {
	
		// hide regions	
		for(i=0;i<this.regions.length;i++) {
			this.disableRegion(i);		
		}

		this.serviceMap = serviceMap;
		
		if(this.serviceMap.mapImage != null) {
		
			// service map object exists
			if(this.refServiceMap != null) {	
				this.updateMapImgItem(this.refServiceMap, this.serviceMap.mapImage);
			}
			else {	
				this.refServiceMap = this.createMapImgItem("serviceMap", this.serviceMap.mapImage);	
			}
		}
		
		// toDo: initialize all services
		this.services = serviceMap.services;
		
		if(this.services != null) {
			// load service images
			for(i=0;i<this.services.length;i++) {
				
				if(this.services[i].westRoute != null) {
					if(this.services[i].westRoute.mapImage != null &&
					   !this.services[i].westRoute.mapImage.imgInit) {	
						this.services[i].westRoute.mapImage.loadImage();
					}
				}
				
				if(this.services[i].eastRoute != null) {	
					if(this.services[i].eastRoute.mapImage != null &&
					   !this.services[i].eastRoute.mapImage.imgInit) {
						this.services[i].eastRoute.mapImage.loadImage();
					}
				}
				
			}
	
			for(i=0;i<this.services.length;i++) {		
				this.setService(i);
			}
		}

		$(this.refWorldMap).fadeOut(400);
		$(this.refServiceMap).fadeIn(400);
		
		this.viewMode = this.C_SERVICE_MAP;	
	}

	
	this.animationWorldMap = function() {
		$(this.refWorldMap).fadeIn(400);
	}
	
	//Calculates the reference point (0,0) related to the map container.	
	this.calcMapRefPoint = function() {
		var p = $(this.id).offset();	
		//this.rp.x = p.left;
		//this.rp.y = p.top; 
		this.rp.x = 0;
		this.rp.y = 0;
	}
	
	
	/*
	 Sets the object with the specified ID to the position (x,y)
	 relative to the mapContainer.
	*/	
	this.placeFeature = function(objectID, x, y) {
			
		var posX = this.rp.x + x;	
		var posY = this.rp.y + y;	
		$(objectID).css({left:posX, top:posY});
	}
	
}

function GuiList(htmlID) {
	
	this.id = htmlID;

	this.eventListener = null;
	
	this.setEventListener = function(listener) {	
		this.eventListener = listener;
	
		var selfRef = this;	
		$(this.id).hover(function () {
						selfRef.eventListener.evtGuiListHoverIn();
					},
					function () { 
  				   		selfRef.eventListener.evtGuiListHoverOut();
		});
	}
	
	/*
	 * Clears the list, i.e. all text, css classes and event trigger are removed 
	 * but not the list items <li>
	 */
	this.clearAll = function() {
		
		var c = $(this.id).children("li").get();
		var o = null;
		
		for(i=0;i<c.length;i++) {			
			o = $(c).get(i);
			$(o).text("");
			$(o).removeClass("selected");
			$(o).removeClass("disabled");
			
			$(o).unbind();
		}
	}
	
	this.clear = function(index) {		
		var c = $(this.id).children("li").get(index);		
		$(c).text("");
	}
	
	this.init = function() {
		
		var c = $(this.id).children("li").get();
		for(i=0;i<c.length;i++) {
			$(c[i]).unbind();
		}
		$(this.id).empty();
		//alert($(this.id).parent().html());	
	}
	
	/*
	 * Pre: objects offer a toString method that is used for display
	 * 
	 */	
	this.setEntries = function(objects) {		
		for(i=0;i<objects.length;i++) {		
			this.setEntry(i, objects[i].toString());		
		}		
	}
	
	this.setSize = function(size) {	
		var c = $(this.id).children("li").get();
		
		// need to delete elements
		if(c != null && size < c.length) {
			
			var diff = c.length-size;		
			for(t=0;t<diff;t++) {			
				$(this.id).children("li:last").remove();			
			}		
		}
		
		// need to add elements
		else if(c != null && size > c.length) {
			
			var diff = size-c.length;		
			for(t=0;t<diff;t++) {			
				$(this.id).append("<li></li>");			
			}		
		}	
	}
	
	this.setEntry = function(index, text) {
		
		var c = $(this.id).children("li").get();
		var li = null;	
		
		// <li> element already exists
		if(index < c.length) {
			//li = $(c).get(index);
			li = c[index];
		}
		// append new li element
		else {
			
			$(this.id).append("<li></li>");
			
			li = $(this.id).children("li").get(index);
		}
		
		this.resetStyle(li);
		$(li).text(text);
		
		var selfRef = this;	
		
		$(li).unbind();
				
		// send events to event listener			
		$(li).hover(function () {
						selfRef.eventListener.evtGuiListEntryHoverIn(index, null);
					},
					function () { 
  				   		selfRef.eventListener.evtGuiListEntryHoverOut(index, null);
		});
					
		$(li).click(function(event){
			event.preventDefault();
			
			selfRef.eventListener.evtGuiListEntryClick(event, index, null);
		});
		
	}
	
	this.hide = function() {
		$(this.id).hide();
	}
	
	this.show = function() {
		$(this.id).show();
	}
	
	this.fadeIn = function() {
		$(this.id).fadeIn();
	}
	
	this.highlightEntry = function(index) {	
		var c = $(this.id).children("li").get(index);
		$(c).addClass("highlighted");
	}
	
	this.unhighlightEntry = function(index) {	
		var c = $(this.id).children("li").get(index);
		$(c).removeClass("highlighted");
	}
	
	this.selectEntry = function(index) {	
		var c = $(this.id).children("li").get(index);
		this.resetStyle(c);
		$(c).addClass("selected");
	}
	
	this.unselectEntry = function(index) {	
		var c = $(this.id).children("li").get(index);
		this.resetStyle(c);
		$(c).removeClass("selected");
	}
	
	this.disableEntry = function(index) {
		var c = $(this.id).children("li").get(index);
		this.resetStyle(c);
		$(c).addClass("disabled");
	}
	
	this.enableEntry = function(index) {	
		var c = $(this.id).children("li").get(index);
		$(c).removeClass("disabled");
	}
	
	this.resetStyle = function(object) {
		$(object).removeClass("disabled");
		$(object).removeClass("selected");
		$(object).removeClass("highlighted");	
	}
		
}


/*
 * Dynamic table. 
 * 
 * The html table represented by the htmlID must contain the following structure:
 * <table>
 * 	<thead>
 * 		<tr></tr>
 * 	</thead>
 * <tbody>
 * </tbody> 
 * </table>
 * 
 */
function GuiTable(htmlID) {
	
	this.id = htmlID;
	this.num = 0;
	
	this.i = 0;
	this.j = 0;
	
	this.refRows = new Array();
	this.refCols = new Array();
	
	this.head = null;
	this.body = null;
	
	
	this.show = function() {		
		$(this.id).show();
	}
	
	this.fadeIn = function() {
		$(this.id).fadeIn();
	}
	
	this.fadeOut = function() {
		$(this.id).fadeOut(200);
	}
	
	this.hide = function() {	
		$(this.id).hide();
	}
	
	this.preInit = function() {
		this.body = $(this.id).children("tbody");
		this.head = $(this.id).children("thead");	
	}
	
	/*
	 * Initializes the table with i rows and j cols
	 * 
	 */
	this.init = function(rows,cols) {
		
		this.i=rows;
		this.j=cols;
		
		var body = $(this.id).children("tbody");
		var head = $(this.id).children("thead");
		
		var head1 = $(head).children("tr").get(0);
		$(head1).empty();
		$(body).empty();

		var refRows = null;
			
		// append header
		for(j=0;j<cols;j++) {		
			$(head1).append("<th></th>");
		}
	
		// rows		
		for(i=0;i<rows;i++) {		
			$(this.id).append("<tr></tr>");
			
			refRows = $(body).children("tr");		
			// cols
			for(j=0;j<cols;j++) {
				if(j==0) {
					//first col			
					$(refRows[i]).append("<td class='strong'></td>");
				}
				else {
					$(refRows[i]).append("<td></td>");
				}
			}	
		}
		// set css class	
		$(body).children("tr:odd").addClass("even");
		$(body).children("tr:even").addClass("odd");		
	}
	
	
	
	this.reset = function(rows,cols) {

		this.i=rows;
		this.j=cols;
		
		var body = $(this.id).children("tbody");
		var head = $(this.id).children("thead");
		
		var head1 = $(head).children("tr").get(0);
		$(head1).empty();
		$(body).empty();

		var refRows = null;
			
		// append header
		for(j=0;j<cols;j++) {		
			$(head1).append("<th></th>");
		}
		
		var row = "<tr><td class='strong'></td>";
		for(j=0;j<(cols-1);j++) {		
			row += "<td></td>";			
		}	
		row+="</tr>";
			
		// rows		
		for(i=0;i<rows;i++) {		
			$(this.id).append(row);
		}
			
		$(body).children("tr:odd").addClass("even");
		$(body).children("tr:even").addClass("odd");		
	}
	
	/*
	 * updates crosstable with new values 
	 */
	this.fillCrossTable = function(topLeftCorner, rowHeader, colHeader, values) {
				
		var rows = values.length;
		var cols = 0;
		
		if(rows > 0){
			cols = values[0].length;
		} 
		
		var body = $(this.id).children("tbody");
		var head = $(this.id).children("thead");
		
		// delete all elements
		var head1 = $(head).children("tr").get(0);
		$(head1).empty();
		$(body).empty();

		
		// header	
		// top left corner (origin of cross table)
		$(head1).append("<th>" + topLeftCorner + "</th>");			
		// col heads
		for(j=0;j<cols;j++) {		
			$(head1).append("<th>" + colHeader[j] + "</th>");
		}
			
		// rows		
		for(i=0;i<rows;i++) {
			
			// build row			
			var row = "<tr><td class='strong'>" + rowHeader[i] + "</td>";
			
			var rowValues = values[i];
			for(j=0;j<cols;j++) {		
				row += "<td>" + rowValues[j] + "</td>";		
			}	
			row+="</tr>";
								
			$(this.id).append(row);
		}
			
		$(body).children("tr:odd").addClass("even");
		$(body).children("tr:even").addClass("odd");		
	}
	
	
	/*
	 * i: header row
	 * j: header column in row i
	 */
	this.setHeader = function(i,j,entry) {
			
		var head = $(this.id).children("thead");
		head = $(head).children("tr").get(i);
		var cols = $(head).children("th");
		
		if(j>=0 && j<cols.length)
			$(cols[j]).text(entry);
	}
	
	/*
	 * not used
	 */
	this.setHeaderRow = function(i,o,entries) {
		
		var head = $(this.id).children("thead");
		head = $(head).children("tr").get(i);
		var cols = $(head).children("th");
			
		for(t=0;t<entries.length;t++) {		
			if(t<cols.length)
				$(cols[t+o]).text(entries[t]);	
		}	
	}
	
	this.setEntry = function(i,j, entry) {
			
		var body = $(this.id).children("tbody");
		var rows = $(body).children("tr");
		
		var cols =  $(rows[i]).children("td");		
		var cell = $(cols[j]);	
		$(cols[j]).text(entry);		
	}	
	
	/*
	 * not used
	 */
	this.setBodyRow = function(i,o,entries) {
		
		if(this.body != null) {
			
			var rows = this.body.children("tr");					
			var cols =  $(rows[i]).children("td");	
	
			var cell = null;
			for(x=0;x<entries.length;x++) {			
				cell = $(cols[x+o]);
				cell.text(entries[x]);			
			}	
		}
	}
}
