From a3543194f9d9d0d1dea5e86a71e6827bb4953af1 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Tue, 25 Oct 2016 15:51:34 +0200
Subject: [PATCH] issue #41, work in progress

submaps are opening from link (when accessing it from coment tab in
admin panel), however centering is still not handled properly
---
 .../mapviewer/services/view/CommentView.java  |  558 +--
 .../services/view/CommentViewFactory.java     |    6 +-
 .../webapp/WEB-INF/components/map/map.xhtml   |    6 +
 .../WEB-INF/components/map/submapPanel.xhtml  |   10 +-
 web/src/main/webapp/admin/comments.xhtml      |    4 +-
 web/src/main/webapp/index.xhtml               |   44 +-
 web/src/main/webapp/resources/js/CustomMap.js | 3476 +++++++++--------
 .../main/webapp/resources/js/GuiConnector.js  |  604 +--
 8 files changed, 2373 insertions(+), 2335 deletions(-)

diff --git a/service/src/main/java/lcsb/mapviewer/services/view/CommentView.java b/service/src/main/java/lcsb/mapviewer/services/view/CommentView.java
index b9b0cc85eb..43033fde72 100644
--- a/service/src/main/java/lcsb/mapviewer/services/view/CommentView.java
+++ b/service/src/main/java/lcsb/mapviewer/services/view/CommentView.java
@@ -1,270 +1,288 @@
-package lcsb.mapviewer.services.view;
-
-import java.io.Serializable;
-
-import lcsb.mapviewer.model.map.Comment;
-
-/**
- * View representation of the Comment.
- * 
- * @author Piotr Gawron
- * 
- */
-
-public class CommentView extends AbstractView<Comment> implements Serializable {
-	
-	/**
-	 * 
-	 */
-	private static final long	serialVersionUID	= 1L;
-
-	/**
-	 * Author of the comment.
-	 */
-	private String						author;
-
-	/**
-	 * Email of the comment.
-	 */
-	private String						email;
-
-	/**
-	 * Content of the comment.
-	 */
-	private String						content;
-
-	/**
-	 * Title of the comment.
-	 */
-	private String						title;
-
-	/**
-	 * Is the comment visible on the map.
-	 */
-	private String						pinned;
-
-	/**
-	 * Is the comment removed.
-	 */
-	private String						removed;
-
-	/**
-	 * X coordinate on the map.
-	 */
-	private String						xCoord;
-
-	/**
-	 * Y coordinate on the map.
-	 */
-	private String						yCoord;
-
-	/**
-	 * Zoom level at which comment should be investigated on the map (to see
-	 * details).
-	 */
-	private String						zoom;
-
-	/**
-	 * Constructor that initialize the view with the data from original comment.
-	 * 
-	 * @param comment
-	 *          data required for initialization
-	 */
-	protected CommentView(final Comment comment) {
-		super(comment);
-	}
-
-	/**
-	 * Default constructor. Should be used only for deserialization.
-	 */
-	protected CommentView() {
-	}
-
-	/**
-	 * @return the author
-	 * @see #author
-	 */
-	public String getAuthor() {
-		return author;
-	}
-
-	/**
-	 * @param author
-	 *          the author to set
-	 * @see #author
-	 */
-	public void setAuthor(String author) {
-		this.author = author;
-	}
-
-	/**
-	 * @return the email
-	 * @see #email
-	 */
-	public String getEmail() {
-		return email;
-	}
-
-	/**
-	 * @param email
-	 *          the email to set
-	 * @see #email
-	 */
-	public void setEmail(String email) {
-		this.email = email;
-	}
-
-	/**
-	 * @return the content
-	 * @see #content
-	 */
-	public String getContent() {
-		return content;
-	}
-
-	/**
-	 * @param content
-	 *          the content to set
-	 * @see #content
-	 */
-	public void setContent(String content) {
-		this.content = content;
-	}
-
-	/**
-	 * @return the title
-	 * @see #title
-	 */
-	public String getTitle() {
-		return title;
-	}
-
-	/**
-	 * @param title
-	 *          the title to set
-	 * @see #title
-	 */
-	public void setTitle(String title) {
-		this.title = title;
-	}
-
-	/**
-	 * @return the pinned
-	 * @see #pinned
-	 */
-	public String getPinned() {
-		return pinned;
-	}
-
-	/**
-	 * @param pinned
-	 *          the pinned to set
-	 * @see #pinned
-	 */
-	public void setPinned(String pinned) {
-		this.pinned = pinned;
-	}
-
-	/**
-	 * @return the removed
-	 * @see #removed
-	 */
-	public String getRemoved() {
-		return removed;
-	}
-
-	/**
-	 * @param removed
-	 *          the removed to set
-	 * @see #removed
-	 */
-	public void setRemoved(String removed) {
-		this.removed = removed;
-	}
-
-	/**
-	 * @return the xCoord
-	 * @see #xCoord
-	 */
-	public String getxCoord() {
-		return xCoord;
-	}
-
-	/**
-	 * @param xCoord
-	 *          the xCoord to set
-	 * @see #xCoord
-	 */
-	public void setxCoord(String xCoord) {
-		this.xCoord = xCoord;
-	}
-
-	/**
-	 * @return the yCoord
-	 * @see #yCoord
-	 */
-	public String getyCoord() {
-		return yCoord;
-	}
-
-	/**
-	 * @param yCoord
-	 *          the yCoord to set
-	 * @see #yCoord
-	 */
-	public void setyCoord(String yCoord) {
-		this.yCoord = yCoord;
-	}
-
-	/**
-	 * @return the zoom
-	 * @see #zoom
-	 */
-	public String getZoom() {
-		return zoom;
-	}
-
-	/**
-	 * @param zoom
-	 *          the zoom to set
-	 * @see #zoom
-	 */
-	public void setZoom(String zoom) {
-		this.zoom = zoom;
-	}
-
-	/**
-	 * @param pinned
-	 *          the pinned to set
-	 * @see #pinned
-	 */
-	public void setPinned(final boolean pinned) {
-		if (pinned) {
-			setPinned("YES");
-		} else {
-			setPinned("NO");
-		}
-	}
-
-	/**
-	 * Sets x coordinate.
-	 * 
-	 * @param x
-	 *          x coordinate to set
-	 * @see #xCoord
-	 */
-	public void setxCoord(double x) {
-		this.xCoord = x + "";
-	}
-
-	/**
-	 * Sets y coordinate.
-	 * 
-	 * @param y
-	 *          y coordinate to set
-	 * @see #yCoord
-	 */
-	public void setyCoord(double y) {
-		this.yCoord = y + "";
-	}
-};
+package lcsb.mapviewer.services.view;
+
+import java.io.Serializable;
+
+import lcsb.mapviewer.model.map.Comment;
+
+/**
+ * View representation of the Comment.
+ * 
+ * @author Piotr Gawron
+ * 
+ */
+
+public class CommentView extends AbstractView<Comment> implements Serializable {
+	
+	/**
+	 * 
+	 */
+	private static final long	serialVersionUID	= 1L;
+
+	/**
+	 * Author of the comment.
+	 */
+	private String						author;
+
+	/**
+	 * Email of the comment.
+	 */
+	private String						email;
+
+	/**
+	 * Content of the comment.
+	 */
+	private String						content;
+
+	/**
+	 * Title of the comment.
+	 */
+	private String						title;
+
+	/**
+	 * Is the comment visible on the map.
+	 */
+	private String						pinned;
+
+	/**
+	 * Is the comment removed.
+	 */
+	private String						removed;
+
+	/**
+	 * X coordinate on the map.
+	 */
+	private String						xCoord;
+
+	/**
+	 * Y coordinate on the map.
+	 */
+	private String						yCoord;
+
+	/**
+	 * Zoom level at which comment should be investigated on the map (to see
+	 * details).
+	 */
+	private String						zoom;
+	
+	private String						submap;
+
+	/**
+	 * Constructor that initialize the view with the data from original comment.
+	 * 
+	 * @param comment
+	 *          data required for initialization
+	 */
+	protected CommentView(final Comment comment) {
+		super(comment);
+	}
+
+	/**
+	 * Default constructor. Should be used only for deserialization.
+	 */
+	protected CommentView() {
+	}
+
+	/**
+	 * @return the author
+	 * @see #author
+	 */
+	public String getAuthor() {
+		return author;
+	}
+
+	/**
+	 * @param author
+	 *          the author to set
+	 * @see #author
+	 */
+	public void setAuthor(String author) {
+		this.author = author;
+	}
+
+	/**
+	 * @return the email
+	 * @see #email
+	 */
+	public String getEmail() {
+		return email;
+	}
+
+	/**
+	 * @param email
+	 *          the email to set
+	 * @see #email
+	 */
+	public void setEmail(String email) {
+		this.email = email;
+	}
+
+	/**
+	 * @return the content
+	 * @see #content
+	 */
+	public String getContent() {
+		return content;
+	}
+
+	/**
+	 * @param content
+	 *          the content to set
+	 * @see #content
+	 */
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	/**
+	 * @return the title
+	 * @see #title
+	 */
+	public String getTitle() {
+		return title;
+	}
+
+	/**
+	 * @param title
+	 *          the title to set
+	 * @see #title
+	 */
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	/**
+	 * @return the pinned
+	 * @see #pinned
+	 */
+	public String getPinned() {
+		return pinned;
+	}
+
+	/**
+	 * @param pinned
+	 *          the pinned to set
+	 * @see #pinned
+	 */
+	public void setPinned(String pinned) {
+		this.pinned = pinned;
+	}
+
+	/**
+	 * @return the removed
+	 * @see #removed
+	 */
+	public String getRemoved() {
+		return removed;
+	}
+
+	/**
+	 * @param removed
+	 *          the removed to set
+	 * @see #removed
+	 */
+	public void setRemoved(String removed) {
+		this.removed = removed;
+	}
+
+	/**
+	 * @return the xCoord
+	 * @see #xCoord
+	 */
+	public String getxCoord() {
+		return xCoord;
+	}
+
+	/**
+	 * @param xCoord
+	 *          the xCoord to set
+	 * @see #xCoord
+	 */
+	public void setxCoord(String xCoord) {
+		this.xCoord = xCoord;
+	}
+
+	/**
+	 * @return the yCoord
+	 * @see #yCoord
+	 */
+	public String getyCoord() {
+		return yCoord;
+	}
+
+	/**
+	 * @param yCoord
+	 *          the yCoord to set
+	 * @see #yCoord
+	 */
+	public void setyCoord(String yCoord) {
+		this.yCoord = yCoord;
+	}
+
+	/**
+	 * @return the zoom
+	 * @see #zoom
+	 */
+	public String getZoom() {
+		return zoom;
+	}
+
+	/**
+	 * @param zoom
+	 *          the zoom to set
+	 * @see #zoom
+	 */
+	public void setZoom(String zoom) {
+		this.zoom = zoom;
+	}
+
+	/**
+	 * @param pinned
+	 *          the pinned to set
+	 * @see #pinned
+	 */
+	public void setPinned(final boolean pinned) {
+		if (pinned) {
+			setPinned("YES");
+		} else {
+			setPinned("NO");
+		}
+	}
+
+	/**
+	 * Sets x coordinate.
+	 * 
+	 * @param x
+	 *          x coordinate to set
+	 * @see #xCoord
+	 */
+	public void setxCoord(double x) {
+		this.xCoord = x + "";
+	}
+
+	/**
+	 * Sets y coordinate.
+	 * 
+	 * @param y
+	 *          y coordinate to set
+	 * @see #yCoord
+	 */
+	public void setyCoord(double y) {
+		this.yCoord = y + "";
+	}
+
+	/**
+	 * @return the submap
+	 * @see #submap
+	 */
+	public String getSubmap() {
+		return submap;
+	}
+
+	/**
+	 * @param submap the submap to set
+	 * @see #submap
+	 */
+	public void setSubmap(String submap) {
+		this.submap = submap;
+	}
+};
diff --git a/service/src/main/java/lcsb/mapviewer/services/view/CommentViewFactory.java b/service/src/main/java/lcsb/mapviewer/services/view/CommentViewFactory.java
index f3cf4961cd..d5c1a44570 100644
--- a/service/src/main/java/lcsb/mapviewer/services/view/CommentViewFactory.java
+++ b/service/src/main/java/lcsb/mapviewer/services/view/CommentViewFactory.java
@@ -87,12 +87,14 @@ public class CommentViewFactory extends AbstractViewFactory<Comment, CommentView
 				}
 			}
 		}
-		result.setTitle((title));
+		result.setTitle(title);
+		if (comment.getSubmodelData() != null) {
+			result.setSubmap(comment.getSubmodelData().getId() + "");
+		}
 		if (coordinates != null) {
 			result.setxCoord(coordinates.getX());
 			result.setyCoord(coordinates.getY());
 			result.setZoom("" + (Configuration.MIN_ZOOM_LEVEL + model.getZoomLevels() - 1));
-
 		}
 		return result;
 	}
diff --git a/web/src/main/webapp/WEB-INF/components/map/map.xhtml b/web/src/main/webapp/WEB-INF/components/map/map.xhtml
index be6009b956..cc80c2722f 100644
--- a/web/src/main/webapp/WEB-INF/components/map/map.xhtml
+++ b/web/src/main/webapp/WEB-INF/components/map/map.xhtml
@@ -87,6 +87,12 @@
 		<p:remoteCommand name="_sendReferenceGenomeDetailRequest" actionListener="#{referenceGenomeMB.requestJavasciptGenomeDetails}" />
 	</h:form>
 
+	<h:form id="createSubmodelDialog">
+		<p:remoteCommand name="_createSubmodelDialog" actionListener="#{mapMB.createSubmodelDialog}" async="false"/>
+	</h:form>
+
+
+
   <!-- loading dialog used for informing user that data is being loaded -->  
   <p:dialog header="Loading" widgetVar="loadingDlg" resizable="false" id="loadingDlg"	
 								showEffect="fade" modal="true" closable="false">	
diff --git a/web/src/main/webapp/WEB-INF/components/map/submapPanel.xhtml b/web/src/main/webapp/WEB-INF/components/map/submapPanel.xhtml
index 304a6d2987..c586e22bb2 100644
--- a/web/src/main/webapp/WEB-INF/components/map/submapPanel.xhtml
+++ b/web/src/main/webapp/WEB-INF/components/map/submapPanel.xhtml
@@ -19,21 +19,13 @@
 	<h:panelGroup layout="block" >
 		<p:dataTable id="submapDataTable" var="submap" value="#{mapMB.mapDataList}">
 
-<!--		 	<p:column sortBy="id" headerText="Id">	
-				<h:outputText id="id" value="#{submap.mapConfig.idObject}" />
-			</p:column> -->	
-
 		 	<p:column sortBy="name" headerText="Name">	
 				<h:outputText id="name" value="#{submap.mapConfig.name}" />
 			</p:column>	
 
-<!--		 	<p:column sortBy="type" headerText="Type">	
-				<h:outputText id="type" value="#{submap.type}" />
-			</p:column> -->	
-
 		 	<p:column headerText="">
 				<p:commandLink actionListener="#{mapMB.createSubmodelDialog}" 
-						oncomplete="GuiConnector.openDialog(#{submap.mapConfig.idObject}); customMap.openLayoutById('#{mapMB.selectedLayoutIdentifier}');" id="opener" ajax="true" icon="ui-icon-document" title="View">
+						oncomplete="GuiConnector.openDialog(#{submap.mapConfig.idObject});" id="opener" ajax="true" icon="ui-icon-document" title="View">
 		 			<f:param name="submodelId" value="#{submap.mapConfig.idObject}"/>
 					<p:graphicImage height="24" 
 										 width="24" 
diff --git a/web/src/main/webapp/admin/comments.xhtml b/web/src/main/webapp/admin/comments.xhtml
index 40bb86deea..7da4892f98 100644
--- a/web/src/main/webapp/admin/comments.xhtml
+++ b/web/src/main/webapp/admin/comments.xhtml
@@ -29,7 +29,9 @@
 					</p:column>	
 	
 					<p:column sortBy="title" headerText="Title">	
-						<h:link	value="#{comment.title}" outcome="/index.xhtml?id=#{mapMB.currentMapId}&amp;x=#{comment.xCoord}&amp;y=#{comment.yCoord}&amp;zoom=#{comment.zoom}&amp;comments=on" target="_map"/>
+						<h:link	value="#{comment.title}" 
+							outcome="/index.xhtml?id=#{mapMB.currentMapId}&amp;x=#{comment.xCoord}&amp;y=#{comment.yCoord}&amp;zoom=#{comment.zoom}&amp;comments=on&amp;submap=#{comment.submap}" 
+							target="_map"/>
 					</p:column>	
 	
 					<p:column sortBy="author" headerText="Author">	
diff --git a/web/src/main/webapp/index.xhtml b/web/src/main/webapp/index.xhtml
index e1ce9d6856..f8b0016b34 100644
--- a/web/src/main/webapp/index.xhtml
+++ b/web/src/main/webapp/index.xhtml
@@ -84,12 +84,14 @@
  	var configuration = new Configuration();
 
 function processUrlGetParams() {
-	if (GuiConnector.getParams["x"]!=undefined && GuiConnector.getParams["y"]!=undefined){
-		ServerConnector.setCenterCoordinateX(GuiConnector.getParams["x"]);
-		ServerConnector.setCenterCoordinateY(GuiConnector.getParams["y"]);
-	}
-	if (GuiConnector.getParams["zoom"]!=undefined ){
-		ServerConnector.setZoomLevel(GuiConnector.getParams["zoom"]);
+	if (GuiConnector.getParams["submap"]==null) {
+		if (GuiConnector.getParams["x"]!=undefined && GuiConnector.getParams["y"]!=undefined){
+			ServerConnector.setCenterCoordinateX(GuiConnector.getParams["x"]);
+			ServerConnector.setCenterCoordinateY(GuiConnector.getParams["y"]);
+		}
+		if (GuiConnector.getParams["zoom"]!=undefined){
+			ServerConnector.setZoomLevel(GuiConnector.getParams["zoom"]);
+		}
 	}
 }
 
@@ -156,6 +158,26 @@ function initMap(){
 	//when I try to hide legend from the beginning or in the same thread it's hidden forever... ;/
 	setTimeout(function() {
 		GuiConnector.hideLegend();
+		var submodelId = GuiConnector.getParams["submap"];
+		if (submodelId!=undefined) {
+			_createSubmodelDialog([{name:'submodelId', value: submodelId}]);
+			//for some reason the call above is not sync (even though it's required)
+			var waitingForResponse = setInterval(function() {
+				if (GuiConnector.getJsPopupForSubmodelId(submodelId)!=null) {
+					GuiConnector.openDialog(submodelId);
+					clearInterval(waitingForResponse);
+					if (GuiConnector.getParams["zoom"]!=undefined){
+						customMap.setZoom(submodelId,parseInt(GuiConnector.getParams["zoom"]));
+					}
+					if (GuiConnector.getParams["x"]!=undefined && GuiConnector.getParams["y"]!=undefined){
+						var x = GuiConnector.getParams["x"];
+						var y = GuiConnector.getParams["y"];
+						var point = new google.maps.Point(x, y);
+						customMap.setCenter(submodelId,point);
+					}
+				}
+			},100);
+		}
 	}, 0);
 }
 
@@ -218,16 +240,6 @@ function removeComment(id) {
 				<p:tab id="infoTab" title="&lt;div class='tngContainer'&gt;&lt;div class='tng'&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='maintabdiv'&gt;&lt;i class='fa fa-info maintab'&gt;&lt;/i&gt;&lt;br&gt;PROJECT&lt;/div&gt;" styleClass="leftTab">	
 					<ui:include src="/WEB-INF/components/map/infoPanel.xhtml" />
 				</p:tab >	
-<!--				
-<p:tab id="sample1" title="&lt;div class='tngContainer'&gt;&lt;div class='tng'&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='maintabdiv'&gt;&lt;i class='fa fa-envelope maintab'&gt;&lt;/i&gt;&lt;br&gt;SAMPLE 1&lt;/div&gt;" styleClass="leftTab hide">
-</p:tab >
-
-<p:tab id="sample2" title="&lt;div class='tngContainer'&gt;&lt;div class='tng'&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='maintabdiv'&gt;&lt;i class='fa fa-envelope maintab'&gt;&lt;/i&gt;&lt;br&gt;SAMPLE 2&lt;/div&gt;" styleClass="leftTab hide">
-</p:tab >
-
-<p:tab id="sample3" title="&lt;div class='tngContainer'&gt;&lt;div class='tng'&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='maintabdiv'&gt;&lt;i class='fa fa-envelope maintab'&gt;&lt;/i&gt;&lt;br&gt;SAMPLE 3&lt;/div&gt;" styleClass="leftTab hide">
-</p:tab >			 
--->		
 			</p:tabView >
 			
 			
diff --git a/web/src/main/webapp/resources/js/CustomMap.js b/web/src/main/webapp/resources/js/CustomMap.js
index 443eb5ab98..5cc4d04870 100644
--- a/web/src/main/webapp/resources/js/CustomMap.js
+++ b/web/src/main/webapp/resources/js/CustomMap.js
@@ -1,1735 +1,1741 @@
-/**
- * Default constructor.
- * 
- * @param globalMap
- *            google.maps.Map object representing the map
- * @param configuration
- *            Configuration object representing our data in the map
- * @param bigButtons
- *            boolean value determining if the buttons on the map should be big,
- *            and if the map is run on the touch interface
- * @param hideDiv
- * 
- */
-function CustomMap(options) {
-  if (!(options instanceof CustomMapOptions)) {
-    options = new CustomMapOptions(options);
-  }
-  AbstractCustomMap.call(this, options);
-
-  // set config parameters
-  this.map = options.getMap();
-
-  if (options.isCustomTouchInterface()) {
-    this._touchInterface = new TouchMap(this);
-  }
-
-  // create function that override primefaces fitBounds with default google
-  // implementation
-  var fitBounds = function(bounds) {
-    var tmp = this.fitBounds;
-    this.fitBounds = google.maps.Map.prototype.fitBounds;
-    this.fitBounds(bounds);
-    this.fitBounds = tmp;
-  };
-  this.map.fitBounds2 = fitBounds;
-
-  this.buttons = [];
-
-  this.createSubmaps();
-
-  this.selectedLayouts = [];
-
-  this.setupLayouts();
-
-  this.createBelt();
-
-  this.customizeGoogleMapView();
-
-  this.createMapChangedCallbacks();
-
-  this.createClientServerListeners();
-
-  this.overlayCollections = [];
-
-  // which submap is active (where user made interaction for the last time)
-  this._activeSubmapId = null;
-
-  this.initialized = true;
-
-  // list of reference genomes
-  this._referenceGenome = [];
-
-  ServerConnector.actualizeSessionData();
-}
-
-CustomMap.prototype = Object.create(AbstractCustomMap.prototype);
-
-CustomMap.prototype.constructor = CustomMap;
-
-CustomMap.prototype.createSubmaps = function() {
-  this.submaps = [];
-  for (var i = 0; i < this.getConfiguration().SUBMODELS.length; i++) {
-    this.submaps.push(new Submap(this,
-        this.getConfiguration().SUBMODELS[i].ID_MODEL));
-  }
-};
-
-CustomMap.prototype.createLogo = function() {
-
-  var logoControlDiv = document.createElement('DIV');
-  var logo = document.createElement('IMG');
-  var url = ServerConnector.getLogoImg();
-  if (!/^(f|ht)tps?:\/\//i.test(url)) {
-    url = GuiConnector.getImgPrefix() + url;
-  }
-  logo.src = url;
-  logo.style.cursor = 'pointer';
-  logo.style.width = "80px";
-  logoControlDiv.appendChild(logo);
-  google.maps.event.addDomListener(logo, 'click', function() {
-    var win = window.open(ServerConnector.getLogoLink(), '_blank');
-    win.focus();
-  });
-  logoControlDiv.index = 0; // used for ordering
-  this.map.controls[google.maps.ControlPosition.LEFT_BOTTOM]
-      .push(logoControlDiv);
-
-  var logoControlDiv = document.createElement('DIV');
-  logoControlDiv.style.padding = '5px';
-
-  var logo = document.createElement('IMG');
-  logo.src = GuiConnector.getImgPrefix()
-      + GuiConnector.getLcsbLogoImg(this.bigButtons);
-  logo.style.cursor = 'pointer';
-  logoControlDiv.appendChild(logo);
-  google.maps.event.addDomListener(logo, 'click', function() {
-    var win = window.open('http://wwwen.uni.lu/lcsb/', '_blank');
-    win.focus();
-  });
-
-  logoControlDiv.index = 1; // used for ordering
-  this.map.controls[google.maps.ControlPosition.RIGHT_BOTTOM]
-      .push(logoControlDiv);
-};
-
-CustomMap.prototype.createBelt = function() {
-  var self = this;
-
-  this.divBelt = document.createElement('DIV');
-  this.divBelt.className = "headerBelt";
-
-  var hideDivButton = document.createElement('DIV');
-  hideDivButton.className = "headerHideDivButton";
-
-  var hideButton = document.createElement('button');
-  hideButton.id = "hide_button";
-  hideButton.className = "headerHideButton";
-  hideButton.innerHTML = "<i class='fa fa-chevron-left'></i>";
-  // when there is no div to hide we should allow hiding
-  if (self.getHideDiv() !== undefined) {
-  hideButton.onclick = (function() {
-    var button = hideButton;
-      var div = self.getHideDiv();
-
-    var left = $(PrimeFaces.escapeClientId(self.map.getDiv().id)).offset().left;
-    return function() {
-      if (button.innerHTML.indexOf('fa-chevron-left') > 0) {
-        button.innerHTML = "<i class='fa fa-chevron-right'></i>";
-        div.style.display = 'none';
-        self.map.getDiv().style.left = "0px";
-      } else {
-        div.style.display = 'block';
-        button.innerHTML = "<i class='fa fa-chevron-left'></i>";
-        self.map.getDiv().style.left = left + "px";
-      }
-      google.maps.event.trigger(self.map, 'resize');
-      return false;
-    };
-  })();
-  } else {
-    hideButton.disabled = true;
-    logger.warn("Left panel hiding disabled");
-  }
-  hideDivButton.appendChild(hideButton);
-  hideDivButton.index = 1; // used for ordering
-  this.divBelt.appendChild(hideDivButton);
-
-  var controlText = document.createElement('div');
-  controlText.className = "headerTextBold";
-  controlText.innerHTML = this.getConfiguration().MAP_NAME;
-  this.divBelt.appendChild(controlText);
-
-  this.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.divBelt);
-};
-
-CustomMap.prototype.setLegendVisible = function(vis) {
-  if (vis) {
-    document.getElementById('legend').style.display = 'block';
-  } else {
-    document.getElementById('legend').style.display = 'none';
-  }
-};
-
-CustomMap.prototype.clearOverlays = function() {
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      var collection = this.overlayCollections[overlayName];
-      this.clearOverlayCollection(collection);
-    }
-  }
-};
-
-CustomMap.prototype.refreshOverlays = function() {
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      var collection = this.overlayCollections[overlayName];
-      collection.refresh();
-    }
-  }
-};
-
-/**
- * Removes all markers from {@link OverlayCollection}.
- * 
- * @param collection
- *            {@link OverlayCollection} from which all markers should be removed
- */
-CustomMap.prototype.clearOverlayCollection = function(collection) {
-  logger.debug("Clear collection: " + collection.name);
-  for ( var key in collection.aliasMarkers) {
-    if (collection.aliasMarkers.hasOwnProperty(key)
-        && collection.aliasMarkers[key] != null) {
-      collection.aliasMarkers[key].setMap(null);
-    }
-  }
-
-  for ( var key in collection.pointMarkers) {
-    if (collection.pointMarkers.hasOwnProperty(key)
-        && collection.pointMarkers[key] != null) {
-
-      collection.pointMarkers[key].setMap(null);
-    }
-  }
-
-  for ( var key in collection.reactionMarkers) {
-    if (collection.reactionMarkers.hasOwnProperty(key)
-        && collection.reactionMarkers[key] != null) {
-      collection.reactionMarkers[key].setMap(null);
-    }
-  }
-
-  collection.aliasMarkers = [];
-  collection.pointMarkers = [];
-  collection.reactionMarkers = [];
-};
-
-/**
- * Updates data about visualized markers in {@link OverlayCollection}.
- * 
- * @param overlayCollection
- *            {@link OverlayCollection} with new data to visualize
- * @param fitBounds
- *            <code>true</code> id the map should fit bounds to the new
- *            elements after update, <code>false</code> otherwise
- */
-CustomMap.prototype.updateOverlayCollection = function(overlayCollection,
-    fitBounds) {
-  this.clearOverlayCollection(overlayCollection);
-  this.renderOverlayCollection(overlayCollection, fitBounds);
-};
-
-/**
- * This method open layout by a given layout identifier (string starting with
- * 'cv' prefix) in a map and all submaps.
- * 
- * @param identifier
- *            identifier of the layout to present
- */
-CustomMap.prototype.openLayout = function(identifier) {
-  logger.debug("Opening layout: " + identifier);
-
-  this.map.setMapTypeId(identifier);
-
-  var index = null;
-  for (var i = 0; i < this.getConfiguration().MAPS.length; i++) {
-    if ('cv' + this.getConfiguration().MAPS[i].idObject == identifier) {
-      index = i;
-    }
-  }
-  if (index == null) {
-    logger.warn("Invalid layout identifier: " + identifier);
-  }
-  for (var i = 0; i < this.submaps.length; i++) {
-    this.submaps[i].openLayout('cv'
-        + this.submaps[i].getConfiguration().MAPS[index].idObject);
-  }
-};
-
-/**
- * This method open layout by a given database identifier.
- * 
- * @param identifier
- *            identifier of the layout to present
- */
-CustomMap.prototype.openLayoutById = function(identifier) {
-  logger.debug("Opening layout: " + identifier);
-  var index = null;
-  for (var i = 0; i < configuration.MAPS.length; i++) {
-    if (configuration.MAPS[i].idObject == identifier) {
-      index = 'cv' + identifier;
-    }
-  }
-
-  // if layout doesn't exist print error
-  if (index == null) {
-    alert("You have no privileges for selected layout");
-  } else {
-    this.openLayout(index);
-  }
-};
-
-CustomMap.prototype.createMapMenu = function(layoutButtons) {
-  var selfMap = this;
-
-  var buttons = [];
-
-  // create a button for overview images when the image is available
-  if (this.getConfiguration().TOP_OVERVIEW_IMAGE != "undefined"
-      && this.getConfiguration().TOP_OVERVIEW_IMAGE != null) {
-    var submenuButtonDiv = document.createElement('button');
-    buttons.push(submenuButtonDiv);
-    submenuButtonDiv.id = "overview_button";
-    submenuButtonDiv.innerHTML = "<i class='fa fa-sitemap' style='font-size:18px; font-weight:400; padding-right:10px;'></i> SHOW OVERVIEW";
-    submenuButtonDiv.className = "overview_button";
-    submenuButtonDiv.onclick = (function() {
-      return function() {
-        selfMap.showOverview();
-        return false;
-      };
-    })();
-    this.divBelt.appendChild(submenuButtonDiv);
-  }
-
-  var rightHeaderMenu = document.createElement('div');
-  rightHeaderMenu.className = "rightHeaderMenu";
-  var submenuDiv = document.createElement('div');
-  submenuDiv.className = "div4checkboxes";
-  var submenuButtonDiv = document.createElement('input');
-  submenuButtonDiv.type = "checkbox";
-  submenuButtonDiv.name = "Comments";
-  submenuButtonDiv.id = "comment_checkbox";
-  submenuButtonDiv.onclick = (function() {
-    var selfButton = submenuButtonDiv;
-    return function() {
-      selfMap.showComments = selfButton.checked;
-      ServerConnector.setShowComments(selfButton.checked);
-      if (selfButton.checked) {
-        document.getElementById('refresh_comments_button').style.display = 'inline';
-      } else {
-        document.getElementById('refresh_comments_button').style.display = 'none';
-      }
-      selfMap.refreshComments();
-
-    };
-  })();
-  var element = document.createElement('label');
-  element.innerHTML = "COMMENTS";
-  element.setAttribute("for", "comment_checkbox");
-  submenuDiv.appendChild(submenuButtonDiv);
-  submenuDiv.appendChild(element);
-
-  var submenuButtonDiv = document.createElement('input');
-  submenuButtonDiv.type = "checkbox";
-  submenuButtonDiv.name = "Legend";
-  submenuButtonDiv.id = "lengend_checkbox";
-  submenuButtonDiv.onclick = (function() {
-    var selfButton = submenuButtonDiv;
-    return function() {
-      if (selfButton.checked) {
-        GuiConnector.showLegend();
-      } else {
-        GuiConnector.hideLegend();
-      }
-    };
-  })();
-  element = document.createElement('label');
-  element.innerHTML = "LEGEND";
-  element.setAttribute("for", "lengend_checkbox");
-  submenuDiv.appendChild(submenuButtonDiv);
-  submenuDiv.appendChild(element);
-
-  submenuButtonDiv = document.createElement('button');
-  submenuButtonDiv.id = "refresh_comments_button";
-  submenuButtonDiv.innerHTML = "<i class='fa fa-refresh' style='font-size:21px; font-weight:400;'></i>";
-  submenuButtonDiv.className = "overview_button";
-  submenuButtonDiv.style.display = 'none';
-  submenuButtonDiv.onclick = (function() {
-    return function() {
-      selfMap.refreshComments();
-      return false;
-    };
-  })();
-  submenuDiv.appendChild(submenuButtonDiv);
-  rightHeaderMenu.appendChild(submenuDiv);
-
-  var submenuButtonDiv = document.createElement('button');
-  buttons.push(submenuButtonDiv);
-  submenuButtonDiv.id = "clear_button";
-  submenuButtonDiv.className = "overview_button";
-  submenuButtonDiv.innerHTML = "<i class='fa fa-times' style='font-size:18px; font-weight:300; padding-right:10px;'></i> CLEAR";
-  submenuButtonDiv.title = "Clear all queries";
-  submenuButtonDiv.style.display = 'inline';
-  submenuButtonDiv.onclick = (function() {
-    return function() {
-      selfMap.clearData();
-      return false;
-    };
-  })();
-  rightHeaderMenu.appendChild(submenuButtonDiv);
-
-  this.divBelt.appendChild(rightHeaderMenu);
-};
-
-CustomMap.prototype.registerSource = function(overlayCollection) {
-  this.overlayCollections[overlayCollection.name] = overlayCollection;
-  overlayCollection.aliasMarkers = [];
-  overlayCollection.pointMarkers = [];
-  overlayCollection.reactionMarkers = [];
-};
-
-CustomMap.prototype.refreshComments = function() {
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)
-        && overlayName == "comment") {
-      var collection = this.overlayCollections[overlayName];
-      collection.refresh();
-      return;
-    }
-  }
-  throw "comment OverlayCollection not found";
-};
-
-CustomMap.prototype.turnOnOffDrawing = function() {
-  var model = this.getSubmodelById(this.getActiveSubmapId());
-  if (model != null) {
-    model._turnOnOffDrawing();
-  } else {
-    throw "Cannot find submodel with id: " + this.getActiveSubmapId();
-  }
-};
-
-CustomMap.prototype.clearData = function() {
-  this.clearOverlays();
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      ServerConnector.sendClearRequest(overlayName);
-    }
-  }
-};
-
-CustomMap.prototype.refreshMarkers = function() {
-  logger.debug("Refresh Markers: ");
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      var collection = this.overlayCollections[overlayName];
-      this.refreshOverlayMarkers(collection);
-    }
-  }
-};
-
-CustomMap.prototype.refreshOverlayMarkers = function(overlay) {
-  var self = this;
-  logger.debug("Refresh overlay: " + overlay.name);
-  var boundsArray = [];
-  boundsArray[this.getId()] = new google.maps.LatLngBounds();
-  for (var i = 0; i < this.submaps.length; i++) {
-    boundsArray[this.submaps[i].getId()] = new google.maps.LatLngBounds();
-  }
-
-  var updated = false;
-  var stillMissing = false;
-  for ( var key in overlay.aliasMarkers) {
-    if (overlay.aliasMarkers.hasOwnProperty(key)
-        && overlay.aliasMarkers[key] != null) {
-      var alias = overlay.aliasMarkers[key];
-      if (alias.getAliasData() == null) {
-        var aliasData = alias.getCustomMap().mapModel.getAliasById(alias
-            .getId());
-        if (aliasData != null) {
-          alias.setAliasData(aliasData);
-          alias.init();
-          alias.show();
-          updated = true;
-          var bounds = alias.getBounds();
-          boundsArray[alias.getCustomMap().getId()].extend(bounds
-              .getNorthEast());
-        } else {
-          stillMissing = true;
-          logger.debug("Cannot show alias marker. Data is still not loaded...");
-        }
-      } else {
-        var bounds = alias.getBounds();
-        if (!this.isMarkerOptimization()) {
-          alias.hide();
-          alias.show();
-        }
-        boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
-        boundsArray[alias.getCustomMap().getId()].extend(bounds.getSouthWest());
-      }
-    }
-  }
-
-  for ( var key in overlay.pointMarkers) {
-    if (overlay.pointMarkers.hasOwnProperty(key)
-        && overlay.pointMarkers[key] != null) {
-      var alias = overlay.pointMarkers[key];
-      // we don't need to update this markers because thet data about
-      // visualization is
-      // already there
-      // alias.update();
-      var bounds = alias.getBounds();
-      if (!this.isMarkerOptimization()) {
-        alias.hide();
-        alias.show();
-      }
-      boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
-    }
-  }
-
-  for ( var key in overlay.reactionMarkers) {
-    if (overlay.reactionMarkers.hasOwnProperty(key)
-        && overlay.reactionMarkers[key] != null) {
-      var reactionOverlay = overlay.reactionMarkers[key];
-      if (reactionOverlay.getReactionData() == null) {
-        var reactionData = reactionOverlay.getCustomMap().mapModel
-            .getReactionById(reactionOverlay.getId());
-        if (reactionData != null) {
-          reactionOverlay.setReactionData(reactionData);
-          reactionOverlay.init();
-          reactionOverlay.show();
-          updated = true;
-          var bounds = reactionOverlay.getBounds();
-          boundsArray[reactionOverlay.getCustomMap().getId()].extend(bounds
-              .getNorthEast());
-          boundsArray[reactionOverlay.getCustomMap().getId()].extend(bounds
-              .getSouthWest());
-        } else {
-          stillMissing = true;
-          logger
-              .debug("Cannot show reaction marker. Data is still not loaded...");
-        }
-      } else {
-        var bounds = alias.getBounds();
-        if (!this.isMarkerOptimization()) {
-          alias.hide();
-          alias.show();
-        }
-        boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
-        boundsArray[alias.getCustomMap().getId()].extend(bounds.getSouthWest());
-      }
-
-    }
-  }
-
-  if (!stillMissing && updated && overlay.fitBounds) {
-    for ( var mapId in boundsArray) {
-      if (boundsArray.hasOwnProperty(mapId)) {
-        var map = this.getSubmodelById(mapId).map;
-        var bounds = boundsArray[mapId];
-        if (map != null && !bounds.isEmpty()) {
-          if (typeof map.fitBounds2 !== "undefined") {
-            map.fitBounds2(bounds);
-          } else {
-            map.fitBounds(bounds);
-          }
-        }
-      }
-    }
-  }
-};
-
-CustomMap.prototype.openSubmodel = function(id, htmlTag, jsVar) {
-  if (jsVar.submapControler == null) {
-    var submap = null;
-    for (var i = 0; i < this.submaps.length; i++) {
-      if (this.submaps[i].getId() == id) {
-        submap = this.submaps[i];
-      }
-    }
-    if (submap == null) {
-      logger.error("Unknown submap for id: " + id);
-    } else {
-      submap.init(htmlTag, jsVar);
-      submap.openLayout(this.map.getMapTypeId());
-      this.refreshOverlays();
-
-      // now we have to visualize layouts
-      var layouts = [];
-
-      // get list of layouts
-      for ( var key in this.selectedLayouts) {
-        if (this.selectedLayouts.hasOwnProperty(key)
-            && this.selectedLayouts[key] == true) {
-          layouts.push(key);
-        }
-      }
-
-      // show layouts that should be visualized (resize or show them)
-      for (var i = 0; i < layouts.length; i++) {
-        var layoutId = layouts[i];
-        submap._showSelectedLayout(layoutId, i, layouts.length);
-      }
-    }
-  }
-  jsVar.show();
-};
-
-CustomMap.prototype.customizeGoogleMapView = function() {
-  var mapOptions = this.creatMapOptions();
-  this.map.setOptions(mapOptions);
-
-  this.createMapMenu(false);
-
-  this.registerMapClickEvents();
-
-  this.createLogo();
-  // this.createMapVersion();
-  google.maps.event.trigger(this.map, 'resize');
-  google.maps.event.trigger(this.map, 'maptypeid_changed');
-  google.maps.event.trigger(this.map, 'projection_changed');
-
-  // center map and zoom in to fit into browser window
-  if (this.getConfiguration().fitMapBounds) {
-    var bounds = new google.maps.LatLngBounds();
-    var point = new google.maps.LatLng(
-        this.getConfiguration().topLeftLatLng.lat,
-        this.getConfiguration().topLeftLatLng.lng);
-    bounds.extend(point);
-
-    point = new google.maps.LatLng(
-        this.getConfiguration().bottomRightLatLng.lat,
-        this.getConfiguration().bottomRightLatLng.lng);
-    bounds.extend(point);
-
-    if (typeof this.map.fitBounds2 !== "undefined") {
-      this.map.fitBounds2(bounds);
-    } else {
-      this.map.fitBounds(bounds);
-    }
-  }
-};
-
-CustomMap.prototype.setCenter = function(mapIdentifier, coordinates) {
-  if (this.getConfiguration().ID_MODEL == mapIdentifier) {
-    this.map.setCenter(coordinates);
-  } else {
-    openDialog(mapIdentifier);
-    for (var i = 0; i < this.submaps.length; i++) {
-      if (this.submaps[i].getId() == mapIdentifier) {
-        this.submaps[i].map.setCenter(coordinates);
-      }
-    }
-  }
-};
-
-CustomMap.prototype.setZoom = function(mapIdentifier, zoom) {
-  if (this.getConfiguration().ID_MODEL == mapIdentifier) {
-    this.map.setZoom(zoom);
-  } else {
-    openDialog(mapIdentifier);
-    for (var i = 0; i < this.submaps.length; i++) {
-      if (this.submaps[i].getId() == mapIdentifier) {
-        this.submaps[i].map.setZoom(zoom);
-      }
-    }
-  }
-};
-
-/**
- * Creates listeners for google.maps.Map object that will actualize the data in
- * user session.
- */
-CustomMap.prototype.createMapChangedCallbacks = function() {
-  var customMapSelf = this;
-  // listener for changing zoom level
-  google.maps.event.addListener(this.map, 'zoom_changed', function() {
-    ServerConnector.setZoomLevel(customMapSelf.map.getZoom());
-    ServerConnector.actualizeSessionData();
-  });
-
-  // if we have zoom level data stored in session then restore it
-  var level = ServerConnector.getZoomLevel();
-  if (parseInt(level) > 0) {
-    level = parseInt(level);
-    this.map.setZoom(level);
-  } else {
-    ServerConnector.setZoomLevel(customMapSelf.map.getZoom());
-  }
-
-  // listener for changing location of the map (moving left/reght/top/bottom
-  google.maps.event.addListener(this.map, 'center_changed', function() {
-    var coord = customMapSelf.map.getCenter();
-    var point = customMapSelf.fromLatLngToPoint(coord);
-    ServerConnector.setCenterCoordinateX(point.x);
-    ServerConnector.setCenterCoordinateY(point.y);
-    ServerConnector.actualizeSessionData();
-  });
-
-  // if we have coordinate data stored in session then restore it
-  var x = ServerConnector.getCenterCoordinateX();
-  var y = ServerConnector.getCenterCoordinateY();
-  if (!isNaN(x) && !isNaN(y)) {
-    var point = new google.maps.Point(x, y);
-    var coord = customMapSelf.fromPointToLatLng(point);
-    customMapSelf.map.setCenter(coord);
-  }
-
-  // listener for changing type of layout
-  google.maps.event.addListener(this.map, 'maptypeid_changed', function() {
-    ServerConnector.setSelectedLayout(customMapSelf.map.getMapTypeId());
-    ServerConnector.actualizeParams();
-  });
-
-  // if we have type of layout stored in the session then restore it
-  var mapType = ServerConnector.getSelectedLayout();
-  if (mapType != "" && mapType != null) {
-    this.openLayout(mapType);
-  }
-};
-
-/**
- * Returns submodel (or this model) by identfier of the model.
- * 
- * @param identifier
- *            identifier of the submodel
- * @returns submodel (or this model) with given identfier of the model
- */
-CustomMap.prototype.getSubmodelById = function(identifier) {
-  if (this.getId() == identifier) {
-    return this;
-  }
-  for (var i = 0; i < this.submaps.length; i++) {
-    if (this.submaps[i].getId() == identifier) {
-      return this.submaps[i];
-    }
-  }
-  return null;
-};
-
-CustomMap.prototype.removeSelection = function() {
-  var model = this.getSubmodelById(this.getActiveSubmapId());
-  if (model != null) {
-    model._removeSelection();
-  } else {
-    throw "Cannot find submodel with id: " + this.getActiveSubmapId();
-  }
-};
-
-/**
- * This method will hide google map view and will present single image overview
- * of the data.
- */
-CustomMap.prototype.showOverview = function(overviewImageId) {
-  overviewDialog.syncWindowResize();
-  if (this.getOverviewDiv() == "undefined" || this.getOverviewDiv() == null) {
-    logger.warn("Cannot show overview, because overview div is  undefined");
-  } else {
-    logger.debug("Show overview");
-    overviewDialog.show();
-
-    // resize dialog
-    var htmlTag = GuiConnector.getOverviewHtmlTag();
-
-    var width = Math.floor(window.innerWidth * 2 / 3);
-    var height = Math.floor(window.innerHeight * 2 / 3);
-
-    htmlTag.style.height = (height + 50) + "px";
-    htmlTag.style.width = (width + 20) + "px";
-
-    var self = this;
-
-    // remove all child nodes from overview div
-    while (this.getOverviewDiv().hasChildNodes()) {
-      this.getOverviewDiv().removeChild(this.getOverviewDiv().lastChild);
-    }
-
-    if (overviewImageId == "undefined" || overviewImageId == null) {
-      this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
-    } else {
-      this.overviewImage = null;
-      for (var i = 0; i < this.getConfiguration().OVERVIEW_IMAGES.length; i++) {
-        if (this.getConfiguration().OVERVIEW_IMAGES[i].idObject == overviewImageId) {
-          this.overviewImage = this.getConfiguration().OVERVIEW_IMAGES[i];
-        }
-      }
-
-      if (this.overviewImage == null) {
-        logger.warn("Unknown overview image with id = " + overviewImageId);
-        this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
-      }
-    }
-
-    // add image to overview div
-    this.overviewImageTag = document.createElement("IMG");
-    this.overviewImageTag.src = "../map_images/" + this.overviewImage.filename;
-    this.getOverviewDiv().appendChild(this.overviewImageTag);
-
-    var ratio = 1.0;
-
-    // check how image should be resized to fit dialog and resize it manually!!!
-    if (width / this.overviewImage.width > height / this.overviewImage.height) {
-      this.overviewImageTag.style.height = height + "px";
-      ratio = height / this.overviewImage.height;
-      width = this.overviewImage.width * ratio;
-
-      htmlTag.style.width = (width + 20) + "px";
-    } else {
-      this.overviewImageTag.style.width = width + "px";
-      ratio = width / this.overviewImage.width;
-      height = this.overviewImage.height * ratio;
-
-      htmlTag.style.height = (height + 50) + "px";
-    }
-
-    // center dialog
-    overviewDialog.jq.css("top", Math.max(0,
-        (($(window).height() - overviewDialog.jq.outerHeight()) / 2)
-            + $(window).scrollTop())
-        + "px");
-    overviewDialog.jq.css("left", Math.max(0,
-        (($(window).width() - overviewDialog.jq.outerWidth()) / 2)
-            + $(window).scrollLeft())
-        + "px");
-
-    // on click event (what should happen when we click on the image)
-    var onclickevent = function getClickPosition(e) {
-      var parentPosition = getPosition(e.currentTarget);
-      var xPosition = e.clientX - parentPosition.x;
-      var yPosition = e.clientY - parentPosition.y;
-
-      var imgWidth = self.overviewImageTag.offsetWidth;
-
-      var currentRatio = imgWidth / self.overviewImage.width;
-
-      var xNormal = xPosition / currentRatio;
-      var yNormal = yPosition / currentRatio;
-      var point = {
-        x : xNormal,
-        y : yNormal
-      };
-
-      var link = null;
-      for (var i = 0; i < self.overviewImage.links.length; i++) {
-        if (pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
-          if (link == null) {
-            link = self.overviewImage.links[i];
-          } else {
-            logger.warn("More than one link found. Skipping");
-          }
-        }
-      }
-      if (link != null) {
-        if (link.type == "OverviewModelLink") {
-          logger.debug("Opening model from overview. ModelId: "
-              + link.modelLinkId);
-          logger.debug("link coordinates [" + link.idObject + "]: "
-              + link.latLng);
-          // TODO min zoom value can be different for every map, it should be
-          // changed in the future
-          self.showModel(link.modelLinkId, link.latLng, link.zoomLevel
-              + self.getConfiguration().MIN_ZOOM);
-          overviewDialog.hide();
-        } else if (link.type == "OverviewImageLink") {
-          logger.debug("Opening image from overview. ImageId: "
-              + link.imageLinkId);
-          self.showOverview(link.imageLinkId);
-        } else if (link.type == "OverviewSearchLink") {
-          logger.debug("Sending search query. Query: " + link.query);
-          searchPanel.search(link.query);
-          overviewDialog.hide();
-        } else {
-          logger.warn("Unknown type of link: " + link.type
-              + ". Don't know what to do... LinkId: " + link.idObject);
-        }
-      }
-    };
-
-    this.overviewImageTag.onclick = onclickevent;
-
-    // resize canvas where on mouse over highligh will appear
-    var canvas = document.getElementById("canvasDebug");
-    canvas.width = width;
-    canvas.height = height;
-    canvas.onclick = onclickevent;
-
-    // in debug mode draw clickable shapes
-    if (DEBUG_ON) {
-      var ctx = canvas.getContext("2d");
-      // clear canvas
-      ctx.clearRect(0, 0, canvas.width, canvas.height);
-      for (var i = 0; i < this.overviewImage.links.length; i++) {
-        ctx.beginPath();
-        var polygon = this.overviewImage.links[i].polygon;
-        for (var j = 0; j < polygon.length; j++) {
-          var x = polygon[j].x * ratio;
-          var y = polygon[j].y * ratio;
-          ctx.moveTo(x, y);
-          x = polygon[(j + 1) % polygon.length].x * ratio;
-          y = polygon[(j + 1) % polygon.length].y * ratio;
-          ctx.lineTo(x, y);
-        }
-        ctx.stroke();
-      }
-    }
-
-    this.overviewImage.mousePos = {
-      x : 0,
-      y : 0
-    };
-
-    // this listener should be called when mouse moves over image, it purpose is
-    // to change coursor to pointer when mouse enters clickable polygon and back
-    // to normal when mouse leaves such region
-    var onmousemove = function getMouseOverPosition(e) {
-      var position = getPosition(e.currentTarget);
-      position.x = e.clientX - position.x;
-      position.y = e.clientY - position.y;
-
-      var imgWidth = self.overviewImageTag.offsetWidth;
-
-      var currentRatio = imgWidth / self.overviewImage.width;
-
-      var xNormal = position.x / currentRatio;
-      var yNormal = position.y / currentRatio;
-      var point = {
-        x : xNormal,
-        y : yNormal
-      };
-
-      if (self.overviewImage.mousePos.x != position.x
-          || self.overviewImage.mousePos.y != position.y) {
-        self.overviewImage.mousePos = position;
-        var link = null;
-        for (var i = 0; i < self.overviewImage.links.length; i++) {
-          if (pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
-            link = self.overviewImage.links[i];
-          }
-        }
-        if (link == null) {
-          e.currentTarget.style.cursor = "auto";
-        } else {
-          e.currentTarget.style.cursor = "pointer";
-        }
-      }
-    };
-
-    // onmousemove listener should be assigned to canvas (which is on top of the
-    // image) and overviewimage (just in case something went wrong with resizing
-    // canvas)
-    canvas.onmousemove = onmousemove;
-    this.overviewImageTag.onmousemove = onmousemove;
-  }
-};
-
-CustomMap.prototype.showModel = function(id, point, zoomLevel) {
-  if (point != "undefined") {
-    this.setCenter(id, point);
-  } else {
-    logger.warn("Center point undefined...");
-  }
-  if (zoomLevel != "undefined") {
-    this.setZoom(id, zoomLevel);
-  } else {
-    logger.warn("Zoom level undefined...");
-  }
-};
-
-/**
- * Adds information about aliases visible in the specific layout.
- * 
- * @param layoutId
- *            identifier of the layout
- * 
- * @param jsonAliases
- *            list of aliases in json format
- * 
- */
-CustomMap.prototype.addAliasesForLayout = function(layoutId, jsonAliases) {
-  logger.debug("Adding aliases for layout: " + layoutId);
-
-  // init layout data
-  if (this.mapModel.getLayoutDataById(layoutId) == null) {
-    this.mapModel.initLayoutData(layoutId);
-    for (var i = 0; i < this.submaps.length; i++) {
-      this.submaps[i].mapModel.initLayoutData(layoutId);
-    }
-  }
-
-  var aliases = JSON.parse(jsonAliases);
-  for (var i = 0; i < aliases.length; i++) {
-    var alias = aliases[i];
-    var model = this.getSubmodelById(alias.modelId);
-    if (model != 'undefined' && model != null) {
-      model.mapModel.addAliasForLayout(layoutId, alias);
-    } else {
-      logger.warn("Unknown model: " + alias.modelId);
-    }
-  }
-
-  this.retrieveMissingAliases();
-}
-
-/**
- * Adds information about aliases.
- * 
- * @param jsonAliases
- *            list of aliases in json format
- * 
- */
-CustomMap.prototype.addAliases = function(aliases) {
-  for (var i = 0; i < aliases.length; i++) {
-    var alias = aliases[i];
-    var model = this.getSubmodelById(alias.modelId);
-    if (model != 'undefined' && model != null) {
-      model.addAlias(alias);
-    } else {
-      logger.warn("Unknown model: " + alias.modelId);
-    }
-  }
-  this.callListeners("onAddAliases");
-}
-
-/**
- * This function will ask server for aliases that should be visualized but the
- * data is still missing on the client side.
- */
-CustomMap.prototype.retrieveMissingAliases = function() {
-  var ids = [];
-  var missing = this.mapModel.getMissingAliasIds();
-  for (var j = 0; j < missing.length; j++) {
-    ids.push([ this.getId(), missing[j] ]);
-  }
-  for (var i = 0; i < this.submaps.length; i++) {
-    missing = this.submaps[i].mapModel.getMissingAliasIds();
-    for (var j = 0; j < missing.length; j++) {
-      ids.push([ this.submaps[i].getId(), missing[j] ]);
-    }
-  }
-  if (ids.length > 0) {
-    // load data from server about missing aliases
-    ServerConnector.retreiveLightAliases(ids);
-  }
-  if (!ServerConnector.isWaitingForData()) {
-    // if we already have everything then just refresh data to be visualized
-    this.refreshSelectedLayouts();
-    //and close "loading" dialog
-    GuiConnector.closeLoadingDialog();
-  }
-}
-
-/**
- * Adds layout to be visualized.
- * 
- * @param identifier
- *            identifier of the layout that should be included in visualization
- */
-CustomMap.prototype.addSelectedLayout = function(identifier, name) {
-  logger.debug("Selecting layout: " + identifier);
-
-  if (this.selectedLayouts[identifier] == true) {
-    logger.warn("Layout " + identifier + " already selected");
-  } else {
-    this.selectedLayouts[identifier] = true;
-
-    // open dialog with info that we are loading data (it takes some time for
-    // bigger layouts on big maps)
-    GuiConnector.openLoadingDialog();
-
-    // if we don't have information about this layout then download it
-    if (this.mapModel.getLayoutDataById(identifier) == null) {
-      // initialize what we can on client side
-      this.mapModel.initLayoutData(identifier, name);
-      for (var i = 0; i < this.submaps.length; i++) {
-        this.submaps[i].mapModel.initLayoutData(identifier, name);
-      }
-
-      // load data from server about this layout
-      ServerConnector.retreiveActiveAliasesForLayout(identifier);
-
-      // load data from server about this layout
-      ServerConnector.retreiveActiveReactionsForLayout(identifier);
-    }
-    if (!ServerConnector.isWaitingForData()) {
-      // if we already loaded the data then just visualize it
-      this.refreshSelectedLayouts();
-      //and close "loading" dialog (if opened)
-      GuiConnector.closeLoadingDialog();
-    }
-    // if we have to load data from server then open info window should be
-    // opened
-    ServerConnector
-        .setVisibleLayouts(JSON.stringify(this.getSelectedLayouts()));
-
-  }
-};
-
-/**
- * Returns list of layouts that are selected and visualized by javascript.
- * 
- * @return array with a list of selected layotus
- * 
- */
-CustomMap.prototype.getSelectedLayouts = function() {
-  var layouts = [];
-
-  // get list of layouts
-  for ( var key in this.selectedLayouts) {
-    if (this.selectedLayouts.hasOwnProperty(key)
-        && this.selectedLayouts[key] == true) {
-      layouts.push(key);
-    }
-  }
-  return layouts;
-}
-
-/**
- * Removes layout from visualization.
- * 
- * @param identifier
- *            identifier of layout to remove
- * 
- */
-CustomMap.prototype.removeSelectedLayout = function(identifier) {
-  logger.debug("Removing layout: " + identifier);
-
-  if (this.selectedLayouts[identifier] != true) {
-    logger.warn("Layout " + identifier + " is not selected");
-  } else {
-    this.selectedLayouts[identifier] = false;
-    this.refreshSelectedLayouts();
-    ServerConnector
-        .setVisibleLayouts(JSON.stringify(this.getSelectedLayouts()));
-  }
-};
-
-/**
- * Refresh visualization of selected layouts.
- */
-CustomMap.prototype.refreshSelectedLayouts = function() {
-  logger.debug("Refreshing layouts");
-  var layouts = this.getSelectedLayouts();
-
-  // show layouts that should be visualized (resize or show them)
-  for (var i = 0; i < layouts.length; i++) {
-    var layoutId = layouts[i];
-    if (this.layoutContainsOverlays(layoutId)) {
-      // resize element on the map
-      this.resizeSelectedLayout(layoutId, i, layouts.length);
-    } else {
-      this.showSelectedLayout(layoutId, i, layouts.length);
-    }
-  }
-
-  // remove layouts that were
-  for ( var key in this.selectedLayoutOverlays) {
-    if (!this.selectedLayouts.hasOwnProperty(key)
-        || this.selectedLayouts[key] == false) {
-      if (this.layoutContainsOverlays(key)) {
-        this.hideSelectedLayout(key);
-      }
-    }
-  }
-  this.refreshInfoWindows();
-};
-
-/**
- * Hides layout from the map and all submaps
- * 
- * @param layoutId
- *            identifier of a layout to hide
- */
-CustomMap.prototype.hideSelectedLayout = function(layoutId) {
-  this._hideSelectedLayout(layoutId);
-  for (var i = 0; i < this.submaps.length; i++) {
-    this.submaps[i]._hideSelectedLayout(layoutId);
-  }
-}
-
-/**
- * Resize(refresh) layout on the map and all submaps. Resizing should be called
- * when number of layouts to visualize change.
- * 
- * @param layoutId
- *            identifier of layout to refresh
- * @param index
- *            position of the layout in list of layouts that we visualize
- * @param length
- *            number of layouts that we currently visualize
- */
-CustomMap.prototype.resizeSelectedLayout = function(layoutId, index, length) {
-  logger.debug("Resize layout: " + layoutId);
-  this._resizeSelectedLayout(layoutId, index, length);
-  for (var i = 0; i < this.submaps.length; i++) {
-    this.submaps[i]._resizeSelectedLayout(layoutId, index, length);
-  }
-}
-
-/**
- * Show layout on the map and all submaps.
- * 
- * @param layoutId
- *            identifier of layout to show
- * @param index
- *            position of the layout in list of layouts that we visualize
- * @param length
- *            number of layouts that we currently visualize
- */
-CustomMap.prototype.showSelectedLayout = function(layoutId, index, length) {
-  logger.debug("Resize layout: " + layoutId);
-  this._showSelectedLayout(layoutId, index, length);
-  for (var i = 0; i < this.submaps.length; i++) {
-    this.submaps[i]._showSelectedLayout(layoutId, index, length);
-  }
-}
-
-/**
- * Adds information about reactions visible in the specific layout.
- * 
- * @param layoutId
- *            identifier of the layout
- * 
- * @param jsonAliases
- *            list of reactions in json format
- * 
- */
-CustomMap.prototype.addReactionsForLayout = function(layoutId, jsonReactions) {
-  logger.debug("Adding reactions for layout: " + layoutId);
-  var reactions = JSON.parse(jsonReactions);
-  for (var i = 0; i < reactions.length; i++) {
-    var reaction = reactions[i];
-    var model = this.getSubmodelById(reaction.modelId);
-    if (model != 'undefined' && model != null) {
-      model.mapModel.addReactionForLayout(layoutId, reaction);
-    } else {
-      logger.warn("Unknown model: " + reaction.modelId);
-    }
-  }
-  this.retrieveMissingReactions();
-}
-
-/**
- * Adds information about reactions.
- * 
- * @param jsonAliases
- *            list of reactions in json format
- * 
- */
-CustomMap.prototype.addReactions = function(jsonReactions) {
-  var reactions = JSON.parse(jsonReactions);
-  for (var i = 0; i < reactions.length; i++) {
-    var reaction = reactions[i];
-    var model = this.getSubmodelById(reaction.modelId);
-    if (model != 'undefined' && model != null) {
-      model.addReaction(reaction);
-    } else {
-      logger.warn("Unknown model: " + reaction.modelId);
-    }
-  }
-  this.callListeners("onAddReactions");
-}
-
-/**
- * This function will ask server for reactions that should be visualized but the
- * data is still missing on the client side.
- */
-CustomMap.prototype.retrieveMissingReactions = function() {
-  var ids = [];
-  var missing = this.mapModel.getMissingReactionIds();
-  for (var j = 0; j < missing.length; j++) {
-    ids.push([ this.getId(), missing[j] ]);
-  }
-  for (var i = 0; i < this.submaps.length; i++) {
-    missing = this.submaps[i].mapModel.getMissingReactionIds();
-    for (var j = 0; j < missing.length; j++) {
-      ids.push([ this.submaps[i].getId(), missing[j] ]);
-    }
-  }
-  if (ids.length > 0) {
-    // load data from server about missing reactions
-    ServerConnector.retreiveLightReactions(ids);
-  }
-  if (!ServerConnector.isWaitingForData()) {
-    // if we already have everything then just refresh data to be visualized
-    this.refreshSelectedLayouts();
-    //and close "loading" dialog (if opened)
-    GuiConnector.closeLoadingDialog();
-  }
-}
-
-/**
- * This method checks if the layout contains any overlays (like AliasOverlay or
- * ReactionOverlay) that is currently visible on the map.
- * 
- * @param layoutId
- *            identifier of the layout
- * @returns {Boolean}: <code>true</code> if the layout contains overlays to
- *          visualize, <code>false</code> otherwise
- */
-CustomMap.prototype.layoutContainsOverlays = function(layoutId) {
-
-  // first, check top map
-  if (this.selectedLayoutOverlays.hasOwnProperty(layoutId)
-      && this.selectedLayoutOverlays[layoutId].length > 0) {
-    return true;
-  }
-
-  // now check all submaps
-  for (var i = 0; i < this.submaps.length; i++) {
-    if (this.submaps[i].initialized) {
-      if (this.submaps[i].selectedLayoutOverlays.hasOwnProperty(layoutId)
-          && this.submaps[i].selectedLayoutOverlays[layoutId].length > 0) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-/**
- * Refresh content of all {@link AliasInfoWindow} in this map and all submaps.
- */
-CustomMap.prototype.refreshInfoWindows = function() {
-  this._refreshInfoWindows();
-  // now check all submaps
-  for (var i = 0; i < this.submaps.length; i++) {
-    this.submaps[i]._refreshInfoWindows();
-  }
-}
-
-/**
- * Opens {@link AliasInfoWindow} for an {@link Alias} in a given model/submodel.
- * 
- * @param aliasId
- *            identifier of {@link Alias}
- * @param modelId
- *            identifier of {@link AbstractCustomMap}
- */
-CustomMap.prototype.openInfoWindowForAlias = function(aliasId, modelId) {
-  logger.debug("Opening info window for alias: " + aliasId + ", model: "
-      + modelId);
-  var model = this.getSubmodelById(modelId);
-  var alias = model.mapModel.getAliasById(aliasId);
-
-  // if we have only simple version of the data then ask server for more details
-  if (alias == null || alias.completness == 'SIMPLE') {
-    logger.debug("Accessing full alias: " + aliasId);
-    var ids = [ [ modelId, aliasId ] ];
-    ServerConnector.retreiveFullAliases(ids);
-  }
-  // open AliasInfoWindow in a right model
-  model._openInfoWindowForAlias(aliasId);
-};
-
-/**
- * Renders markers, lines, etc. for elements highlighted in OverlayCollection.
- * 
- * @param overlayCollection
- *            {@link OverlayCollection} to be processed
- * @param fitBounds
- *            <code>true</code> if the borders should fit bounds after
- *            creating all elements
- */
-CustomMap.prototype.renderOverlayCollection = function(overlayCollection,
-    fitBounds) {
-  var elements = overlayCollection.elements;
-  var missingElements = false;
-
-  var boundsArray = [];
-  boundsArray[this.getId()] = new google.maps.LatLngBounds();
-  for (var i = 0; i < this.submaps.length; i++) {
-    boundsArray[this.submaps[i].getId()] = new google.maps.LatLngBounds();
-  }
-
-  for (var i = 0; i < elements.length; i++) {
-    var element = elements[i];
-    var model = this.getSubmodelById(element.modelId);
-    if (element.type == "ALIAS") {
-      if (overlayCollection.aliasMarkers[element.getId()] != null) {
-        logger.warn("More than one marker in " + overlayCollection.name
-            + " for alias " + element.getId() + ". Skipping duplicates.");
-      } else {
-        var aliasData = model.mapModel.getAliasById(element.getId());
-        if (aliasData == null) {
-          model.mapModel.addMissingAliasId(element.getId());
-          missingElements = true;
-        }
-        var marker = new AliasMarker(element.getId(), element.icon, aliasData,
-            model);
-        overlayCollection.aliasMarkers[element.getId()] = marker;
-        if (!missingElements) {
-          var bounds = marker.getBounds();
-          boundsArray[element.modelId].extend(bounds.getNorthEast());
-          boundsArray[element.modelId].extend(bounds.getSouthWest());
-        }
-      }
-    } else if (element.type == "REACTION") {
-      var reactionData = model.mapModel.getReactionById(element.getId());
-      if (reactionData == null) {
-        model.mapModel.addMissingReactionId(element.getId());
-        missingElements = true;
-      }
-      var marker = null;
-      var icon = element.getIcon();
-
-      if (icon === null || icon === undefined) {
-        // this should happen when we visualize search data (there is
-        // no marker, but only flat overlay of the reaction lines)
-        //
-        marker = new ReactionOverlay(null, reactionData, model, false, element
-            .getId());
-      } else {
-        // when we have icon defined (for instance when it comes from
-        // comment) then we don't want to have overlayed reaction lines
-        // but icon marker
-        marker = new ReactionMarker(element.getId(), icon, reactionData, model);
-      }
-      overlayCollection.reactionMarkers[element.getId()] = marker;
-      if (!missingElements) {
-        var bounds = marker.getBounds();
-        boundsArray[element.modelId].extend(bounds.getNorthEast());
-        boundsArray[element.modelId].extend(bounds.getSouthWest());
-
-      }
-    } else if (element.type == "POINT") {
-      var pointData = model.mapModel.getPointDataByPoint(element.getPoint());
-      var marker = new PointMarker(pointData, element.icon, model);
-      overlayCollection.pointMarkers[pointData.getId()] = marker;
-      if (!missingElements) {
-        var bounds = marker.getBounds();
-        boundsArray[element.modelId].extend(bounds.getNorthEast());
-        boundsArray[element.modelId].extend(bounds.getSouthWest());
-      }
-    } else {
-      logger.warn("Unknown type of the element in overlay: " + element.type);
-    }
-    var infoWindow = this.getInfoWindowForIdentifiedElement(element);
-    if (infoWindow != null) {
-      this.retrieveOverlayDetailDataForElement(element, infoWindow
-          .getOverlayFullViewArray());
-      this.updateInfoWindowForIdentifiedElement(element);
-    }
-  }
-
-  if (missingElements) {
-    this.retrieveMissingReactions();
-    this.retrieveMissingAliases();
-  } else {
-    if (elements.length > 0 && fitBounds) {
-      for ( var mapId in boundsArray) {
-        if (boundsArray.hasOwnProperty(mapId)) {
-          var map = this.getSubmodelById(mapId).map;
-          var bounds = boundsArray[mapId];
-          if (map != null && !bounds.isEmpty()) {
-            if (typeof map.fitBounds2 !== "undefined") {
-              map.fitBounds2(bounds);
-            } else {
-              map.fitBounds(bounds);
-            }
-          }
-        }
-      }
-    }
-  }
-};
-
-/**
- * Creates and register listeners to be called on events:
- * <ul>
- * <li>onAddAliases</li>
- * <li>onAddReactions</li>
- * </ul>
- */
-CustomMap.prototype.createClientServerListeners = function() {
-  this.registerListenerType("onAddAliases");
-  this.registerListenerType("onAddReactions");
-
-  var refreshLayoutsFun = function(e) {
-    var self = e.object;
-    if (!ServerConnector.isWaitingForData()) {
-      self.refreshSelectedLayouts();
-      //and close "loading" dialog (if opened)
-      GuiConnector.closeLoadingDialog();
-    }
-  };
-
-  var refreshOverlaysFun = function(e) {
-    e.object.refreshMarkers();
-  };
-
-  this.addListener("onAddAliases", refreshLayoutsFun);
-  this.addListener("onAddAliases", refreshOverlaysFun);
-
-  this.addListener("onAddReactions", refreshLayoutsFun);
-  this.addListener("onAddReactions", refreshOverlaysFun);
-
-};
-
-/**
- * Opens {@link AbstractInfoWindow} for a marker.
- * 
- * @param marker
- *            {@link AbstractMarker} for which info window should be opened
- */
-CustomMap.prototype.openInfoWindowForMarker = function(marker) {
-  var modelId = marker.getCustomMap().getId();
-  var markerId = marker.getId();
-  var model = this.getSubmodelById(modelId);
-  logger.debug("Opening info window for " + marker.constructor.name + ": "
-      + markerId + ", model: " + modelId);
-  var elementType = marker.getType();
-  if (marker instanceof AliasMarker) {
-    var alias = model.mapModel.getAliasById(markerId);
-
-    // if we have only simple version of the data then ask server for more
-    // details
-    if (alias == null || alias.completness == 'SIMPLE') {
-      logger.debug("Accessing full alias: " + markerId);
-      var ids = [ [ modelId, markerId ] ];
-      ServerConnector.retreiveFullAliases(ids);
-    }
-  } else if (marker instanceof PointMarker) {
-    // no special treatment for points
-  } else if (marker instanceof ReactionMarker) {
-    // no special treatment for reactions
-  } else {
-    logger.error("Unknown marker type: " + marker.constructor.name);
-  }
-
-  // open AliasInfoWindow in a right model
-  model._openInfoWindowForMarker(marker);
-
-  var infoWindow = model.returnInfoWindowForMarker(marker);
-
-  var element = new IdentifiedElement({
-    objectId : markerId,
-    modelId : modelId,
-    type : elementType
-  });
-
-  this.retrieveOverlayDetailDataForElement(element, infoWindow
-      .getOverlayFullViewArray());
-
-};
-
-/**
- * Sends requestes to download detailed data about element in all
- * {@link OverlayCollection}.
- * 
- * @param element
- *            element for which we want to have detailed information
- */
-CustomMap.prototype.retrieveOverlayDetailDataForElement = function(element,
-    general) {
-  if (general === undefined) {
-    logger.warn("general param is undefined!");
-    general = [];
-  }
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      var overlay = this.overlayCollections[overlayName];
-
-      var generalRequest = general[overlayName];
-      if (generalRequest === undefined) {
-        logger.warn(
-            "No information about general overlay request for overlay: ",
-            overlayName);
-        generalRequest = false;
-      }
-      generalRequest = generalRequest || !overlay.allowSearchById();
-
-      if (overlay.allowGeneralSearch() || overlay.allowSearchById()) {
-        if (overlay.isMissingDetailData(element, generalRequest)) {
-          ServerConnector.sendOverlayDetailDataRequest(overlayName, element,
-              generalRequest);
-        }
-      }
-    }
-  }
-};
-
-/**
- * Updates info window identified by element given as a parameter.
- * 
- * @param identifiedElement
- *            element for which info window should be updated
- */
-CustomMap.prototype.updateInfoWindowForIdentifiedElement = function(
-    identifiedElement) {
-  var infoWindow = this.getInfoWindowForIdentifiedElement(identifiedElement);
-  if (infoWindow == null) {
-    return;
-  } else {
-    infoWindow.update();
-  }
-};
-
-/**
- * Returns data from all {@link OverlayCollection} for a given alias.
- * 
- * @param alias
- *            {@link Alias} for which overlay data will be returned
- * @param general
- *            if true then all elements will be returned, if false then only
- *            ones availble right now in the overlay
- * @returns data from all {@link OverlayCollection} for a given alias
- */
-CustomMap.prototype.getOverlayDataForAlias = function(alias, general) {
-  var identifiedElement = new IdentifiedElement(alias);
-  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
-};
-
-/**
- * Returns data from all {@link OverlayCollection} for a given reaction.
- * 
- * @param reaction
- *            {@link Reaction} for which overlay data will be returned
- * @param general
- *            if true then all elements will be returned, if false then only
- *            ones availble right now in the overlay
- * @returns data from all {@link OverlayCollection} for a given alias
- */
-CustomMap.prototype.getOverlayDataForReaction = function(reaction, general) {
-  var identifiedElement = new IdentifiedElement(reaction);
-  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
-};
-
-/**
- * Returns data from all {@link OverlayCollection} for a given {@link PointData}
- * 
- * @param point
- *            {@link PointData} for which overlay data will be returned
- * @returns data from all {@link OverlayCollection} for a given
- *          {@link PointData}
- */
-CustomMap.prototype.getOverlayDataForPoint = function(point, general) {
-  var identifiedElement = new IdentifiedElement(point);
-  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
-};
-
-/**
- * Returns data from all {@link OverlayCollection} for element identified by the
- * parameter
- * 
- * @param identifiedElement
- *            {@link IdentifiedElement} for which overlay data will be returned
- * @returns data from all {@link OverlayCollection} for a given
- *          {@link IdentifiedElement}
- */
-CustomMap.prototype.getOverlayDataForIdentifiedElement = function(
-    identifiedElement, general) {
-  if (general === undefined) {
-    logger.warn("general parameter must be defined");
-    general = [];
-  }
-  var result = [];
-  for ( var overlayName in this.overlayCollections) {
-    if (this.overlayCollections.hasOwnProperty(overlayName)) {
-      var overlay = this.overlayCollections[overlayName];
-      if (overlay.allowGeneralSearch() || overlay.allowSearchById()) {
-        var generalFlag = general[overlay.getName()];
-        if (generalFlag === undefined) {
-          logger.warn("General flag for overlay: " + overlay.getName()
-              + " is not defined, assuming false");
-          generalFlag = false;
-        }
-        result.push({
-          overlay : overlay,
-          data : overlay.getDetailDataByIdentifiedElement(identifiedElement,
-              !overlay.allowSearchById() || generalFlag)
-        });
-      }
-    }
-  }
-  return result;
-};
-
-/**
- * Returns {@link AbstractInfoWindow} for element identified by the parameter.
- * 
- * @param identifiedElement
- *            {@link IdentifiedElement} that determines for which element we
- *            want {@link AbstractInfoWindow}
- * @returns {@link AbstractInfoWindow} for element identified by the parameter
- */
-CustomMap.prototype.getInfoWindowForIdentifiedElement = function(
-    identifiedElement) {
-  var model = this.getSubmodelById(identifiedElement.modelId);
-  var infoWindow = null;
-  if (identifiedElement.type == "ALIAS") {
-    infoWindow = model.getAliasInfoWindowById(identifiedElement.getId());
-  } else if (identifiedElement.type == "POINT") {
-    infoWindow = model.getPointInfoWindowById(identifiedElement.getId());
-  } else if (identifiedElement.type == "REACTION") {
-    infoWindow = model.getReactionInfoWindowById(identifiedElement.getId());
-  } else {
-    logger.error("Unknown type of IdentifiedElement: ", identifiedElement);
-  }
-  return infoWindow;
-};
-
-CustomMap.prototype.getActiveSubmapId = function() {
-  return this._activeSubmapId;
-};
-
-CustomMap.prototype.setActiveSubmapId = function(submapId) {
-  this._activeSubmapId = submapId;
-};
-
-CustomMap.prototype.updateAliasesForLayout = function(layoutId, jsonAliases) {
-  logger.debug("Updating aliases for layout: " + layoutId);
-
-  // init layout data
-  if (this.mapModel.getLayoutDataById(layoutId) == null) {
-    this.mapModel.initLayoutData(layoutId);
-    for (var i = 0; i < this.submaps.length; i++) {
-      this.submaps[i].mapModel.initLayoutData(layoutId);
-    }
-  }
-
-  var aliases = JSON.parse(jsonAliases);
-  for (var i = 0; i < aliases.length; i++) {
-    var alias = aliases[i];
-    var model = this.getSubmodelById(alias.modelId);
-    if (model != 'undefined' && model != null) {
-      model.mapModel.updateAliasForLayout(layoutId, alias);
-      model.getAliasInfoWindowById(alias.idObject).update();
-    } else {
-      logger.warn("Unknown model: " + alias.modelId);
-    }
-  }
-
-  this.retrieveMissingAliases();
-};
-
-CustomMap.prototype.getReferenceGenome = function(type, version) {
-  var result = null;
-  if (this._referenceGenome[type] == null) {
-    this._referenceGenome[type] = [];
-  }
-  if (this._referenceGenome[type][version] == null) {
-    ServerConnector.sendReferenceGenomeDetailRequest(type, version);
-    this._referenceGenome[type][version] = new ReferenceGenome(null);
-    return null;
-  } else {
-    return this._referenceGenome[type][version];
-  }
-};
-
-CustomMap.prototype.updateReferenceGenome = function(type, version, jsonObj) {
-  if (this._referenceGenome[type] == null) {
-    this._referenceGenome[type] = [];
-  }
-  this._referenceGenome[type][version] = new ReferenceGenome(jsonObj);
-  this.refreshInfoWindows();
-}
+/**
+ * Default constructor.
+ * 
+ * @param globalMap
+ *            google.maps.Map object representing the map
+ * @param configuration
+ *            Configuration object representing our data in the map
+ * @param bigButtons
+ *            boolean value determining if the buttons on the map should be big,
+ *            and if the map is run on the touch interface
+ * @param hideDiv
+ * 
+ */
+function CustomMap(options) {
+  if (!(options instanceof CustomMapOptions)) {
+    options = new CustomMapOptions(options);
+  }
+  AbstractCustomMap.call(this, options);
+
+  // set config parameters
+  this.map = options.getMap();
+
+  if (options.isCustomTouchInterface()) {
+    this._touchInterface = new TouchMap(this);
+  }
+
+  // create function that override primefaces fitBounds with default google
+  // implementation
+  var fitBounds = function(bounds) {
+    var tmp = this.fitBounds;
+    this.fitBounds = google.maps.Map.prototype.fitBounds;
+    this.fitBounds(bounds);
+    this.fitBounds = tmp;
+  };
+  this.map.fitBounds2 = fitBounds;
+
+  this.buttons = [];
+
+  this.createSubmaps();
+
+  this.selectedLayouts = [];
+
+  this.setupLayouts();
+
+  this.createBelt();
+
+  this.customizeGoogleMapView();
+
+  this.createMapChangedCallbacks();
+
+  this.createClientServerListeners();
+
+  this.overlayCollections = [];
+
+  // which submap is active (where user made interaction for the last time)
+  this._activeSubmapId = null;
+
+  this.initialized = true;
+
+  // list of reference genomes
+  this._referenceGenome = [];
+
+  ServerConnector.actualizeSessionData();
+}
+
+CustomMap.prototype = Object.create(AbstractCustomMap.prototype);
+
+CustomMap.prototype.constructor = CustomMap;
+
+CustomMap.prototype.createSubmaps = function() {
+  this.submaps = [];
+  for (var i = 0; i < this.getConfiguration().SUBMODELS.length; i++) {
+    this.submaps.push(new Submap(this,
+        this.getConfiguration().SUBMODELS[i].ID_MODEL));
+  }
+};
+
+CustomMap.prototype.createLogo = function() {
+
+  var logoControlDiv = document.createElement('DIV');
+  var logo = document.createElement('IMG');
+  var url = ServerConnector.getLogoImg();
+  if (!/^(f|ht)tps?:\/\//i.test(url)) {
+    url = GuiConnector.getImgPrefix() + url;
+  }
+  logo.src = url;
+  logo.style.cursor = 'pointer';
+  logo.style.width = "80px";
+  logoControlDiv.appendChild(logo);
+  google.maps.event.addDomListener(logo, 'click', function() {
+    var win = window.open(ServerConnector.getLogoLink(), '_blank');
+    win.focus();
+  });
+  logoControlDiv.index = 0; // used for ordering
+  this.map.controls[google.maps.ControlPosition.LEFT_BOTTOM]
+      .push(logoControlDiv);
+
+  var logoControlDiv = document.createElement('DIV');
+  logoControlDiv.style.padding = '5px';
+
+  var logo = document.createElement('IMG');
+  logo.src = GuiConnector.getImgPrefix()
+      + GuiConnector.getLcsbLogoImg(this.bigButtons);
+  logo.style.cursor = 'pointer';
+  logoControlDiv.appendChild(logo);
+  google.maps.event.addDomListener(logo, 'click', function() {
+    var win = window.open('http://wwwen.uni.lu/lcsb/', '_blank');
+    win.focus();
+  });
+
+  logoControlDiv.index = 1; // used for ordering
+  this.map.controls[google.maps.ControlPosition.RIGHT_BOTTOM]
+      .push(logoControlDiv);
+};
+
+CustomMap.prototype.createBelt = function() {
+  var self = this;
+
+  this.divBelt = document.createElement('DIV');
+  this.divBelt.className = "headerBelt";
+
+  var hideDivButton = document.createElement('DIV');
+  hideDivButton.className = "headerHideDivButton";
+
+  var hideButton = document.createElement('button');
+  hideButton.id = "hide_button";
+  hideButton.className = "headerHideButton";
+  hideButton.innerHTML = "<i class='fa fa-chevron-left'></i>";
+  // when there is no div to hide we should allow hiding
+  if (self.getHideDiv() !== undefined) {
+  hideButton.onclick = (function() {
+    var button = hideButton;
+      var div = self.getHideDiv();
+
+    var left = $(PrimeFaces.escapeClientId(self.map.getDiv().id)).offset().left;
+    return function() {
+      if (button.innerHTML.indexOf('fa-chevron-left') > 0) {
+        button.innerHTML = "<i class='fa fa-chevron-right'></i>";
+        div.style.display = 'none';
+        self.map.getDiv().style.left = "0px";
+      } else {
+        div.style.display = 'block';
+        button.innerHTML = "<i class='fa fa-chevron-left'></i>";
+        self.map.getDiv().style.left = left + "px";
+      }
+      google.maps.event.trigger(self.map, 'resize');
+      return false;
+    };
+  })();
+  } else {
+    hideButton.disabled = true;
+    logger.warn("Left panel hiding disabled");
+  }
+  hideDivButton.appendChild(hideButton);
+  hideDivButton.index = 1; // used for ordering
+  this.divBelt.appendChild(hideDivButton);
+
+  var controlText = document.createElement('div');
+  controlText.className = "headerTextBold";
+  controlText.innerHTML = this.getConfiguration().MAP_NAME;
+  this.divBelt.appendChild(controlText);
+
+  this.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.divBelt);
+};
+
+CustomMap.prototype.setLegendVisible = function(vis) {
+  if (vis) {
+    document.getElementById('legend').style.display = 'block';
+  } else {
+    document.getElementById('legend').style.display = 'none';
+  }
+};
+
+CustomMap.prototype.clearOverlays = function() {
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      var collection = this.overlayCollections[overlayName];
+      this.clearOverlayCollection(collection);
+    }
+  }
+};
+
+CustomMap.prototype.refreshOverlays = function() {
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      var collection = this.overlayCollections[overlayName];
+      collection.refresh();
+    }
+  }
+};
+
+/**
+ * Removes all markers from {@link OverlayCollection}.
+ * 
+ * @param collection
+ *            {@link OverlayCollection} from which all markers should be removed
+ */
+CustomMap.prototype.clearOverlayCollection = function(collection) {
+  logger.debug("Clear collection: " + collection.name);
+  for ( var key in collection.aliasMarkers) {
+    if (collection.aliasMarkers.hasOwnProperty(key)
+        && collection.aliasMarkers[key] != null) {
+      collection.aliasMarkers[key].setMap(null);
+    }
+  }
+
+  for ( var key in collection.pointMarkers) {
+    if (collection.pointMarkers.hasOwnProperty(key)
+        && collection.pointMarkers[key] != null) {
+
+      collection.pointMarkers[key].setMap(null);
+    }
+  }
+
+  for ( var key in collection.reactionMarkers) {
+    if (collection.reactionMarkers.hasOwnProperty(key)
+        && collection.reactionMarkers[key] != null) {
+      collection.reactionMarkers[key].setMap(null);
+    }
+  }
+
+  collection.aliasMarkers = [];
+  collection.pointMarkers = [];
+  collection.reactionMarkers = [];
+};
+
+/**
+ * Updates data about visualized markers in {@link OverlayCollection}.
+ * 
+ * @param overlayCollection
+ *            {@link OverlayCollection} with new data to visualize
+ * @param fitBounds
+ *            <code>true</code> id the map should fit bounds to the new
+ *            elements after update, <code>false</code> otherwise
+ */
+CustomMap.prototype.updateOverlayCollection = function(overlayCollection,
+    fitBounds) {
+  this.clearOverlayCollection(overlayCollection);
+  this.renderOverlayCollection(overlayCollection, fitBounds);
+};
+
+/**
+ * This method open layout by a given layout identifier (string starting with
+ * 'cv' prefix) in a map and all submaps.
+ * 
+ * @param identifier
+ *            identifier of the layout to present
+ */
+CustomMap.prototype.openLayout = function(identifier) {
+  logger.debug("Opening layout: " + identifier);
+
+  this.map.setMapTypeId(identifier);
+
+  var index = null;
+  for (var i = 0; i < this.getConfiguration().MAPS.length; i++) {
+    if ('cv' + this.getConfiguration().MAPS[i].idObject == identifier) {
+      index = i;
+    }
+  }
+  if (index == null) {
+    logger.warn("Invalid layout identifier: " + identifier);
+  }
+  for (var i = 0; i < this.submaps.length; i++) {
+    this.submaps[i].openLayout('cv'
+        + this.submaps[i].getConfiguration().MAPS[index].idObject);
+  }
+};
+
+/**
+ * This method open layout by a given database identifier.
+ * 
+ * @param identifier
+ *            identifier of the layout to present
+ */
+CustomMap.prototype.openLayoutById = function(identifier) {
+  logger.debug("Opening layout: " + identifier);
+  var index = null;
+  for (var i = 0; i < configuration.MAPS.length; i++) {
+    if (configuration.MAPS[i].idObject == identifier) {
+      index = 'cv' + identifier;
+    }
+  }
+
+  // if layout doesn't exist print error
+  if (index == null) {
+    alert("You have no privileges for selected layout");
+  } else {
+    this.openLayout(index);
+  }
+};
+
+CustomMap.prototype.createMapMenu = function(layoutButtons) {
+  var selfMap = this;
+
+  var buttons = [];
+
+  // create a button for overview images when the image is available
+  if (this.getConfiguration().TOP_OVERVIEW_IMAGE != "undefined"
+      && this.getConfiguration().TOP_OVERVIEW_IMAGE != null) {
+    var submenuButtonDiv = document.createElement('button');
+    buttons.push(submenuButtonDiv);
+    submenuButtonDiv.id = "overview_button";
+    submenuButtonDiv.innerHTML = "<i class='fa fa-sitemap' style='font-size:18px; font-weight:400; padding-right:10px;'></i> SHOW OVERVIEW";
+    submenuButtonDiv.className = "overview_button";
+    submenuButtonDiv.onclick = (function() {
+      return function() {
+        selfMap.showOverview();
+        return false;
+      };
+    })();
+    this.divBelt.appendChild(submenuButtonDiv);
+  }
+
+  var rightHeaderMenu = document.createElement('div');
+  rightHeaderMenu.className = "rightHeaderMenu";
+  var submenuDiv = document.createElement('div');
+  submenuDiv.className = "div4checkboxes";
+  var submenuButtonDiv = document.createElement('input');
+  submenuButtonDiv.type = "checkbox";
+  submenuButtonDiv.name = "Comments";
+  submenuButtonDiv.id = "comment_checkbox";
+  submenuButtonDiv.onclick = (function() {
+    var selfButton = submenuButtonDiv;
+    return function() {
+      selfMap.showComments = selfButton.checked;
+      ServerConnector.setShowComments(selfButton.checked);
+      if (selfButton.checked) {
+        document.getElementById('refresh_comments_button').style.display = 'inline';
+      } else {
+        document.getElementById('refresh_comments_button').style.display = 'none';
+      }
+      selfMap.refreshComments();
+
+    };
+  })();
+  var element = document.createElement('label');
+  element.innerHTML = "COMMENTS";
+  element.setAttribute("for", "comment_checkbox");
+  submenuDiv.appendChild(submenuButtonDiv);
+  submenuDiv.appendChild(element);
+
+  var submenuButtonDiv = document.createElement('input');
+  submenuButtonDiv.type = "checkbox";
+  submenuButtonDiv.name = "Legend";
+  submenuButtonDiv.id = "lengend_checkbox";
+  submenuButtonDiv.onclick = (function() {
+    var selfButton = submenuButtonDiv;
+    return function() {
+      if (selfButton.checked) {
+        GuiConnector.showLegend();
+      } else {
+        GuiConnector.hideLegend();
+      }
+    };
+  })();
+  element = document.createElement('label');
+  element.innerHTML = "LEGEND";
+  element.setAttribute("for", "lengend_checkbox");
+  submenuDiv.appendChild(submenuButtonDiv);
+  submenuDiv.appendChild(element);
+
+  submenuButtonDiv = document.createElement('button');
+  submenuButtonDiv.id = "refresh_comments_button";
+  submenuButtonDiv.innerHTML = "<i class='fa fa-refresh' style='font-size:21px; font-weight:400;'></i>";
+  submenuButtonDiv.className = "overview_button";
+  submenuButtonDiv.style.display = 'none';
+  submenuButtonDiv.onclick = (function() {
+    return function() {
+      selfMap.refreshComments();
+      return false;
+    };
+  })();
+  submenuDiv.appendChild(submenuButtonDiv);
+  rightHeaderMenu.appendChild(submenuDiv);
+
+  var submenuButtonDiv = document.createElement('button');
+  buttons.push(submenuButtonDiv);
+  submenuButtonDiv.id = "clear_button";
+  submenuButtonDiv.className = "overview_button";
+  submenuButtonDiv.innerHTML = "<i class='fa fa-times' style='font-size:18px; font-weight:300; padding-right:10px;'></i> CLEAR";
+  submenuButtonDiv.title = "Clear all queries";
+  submenuButtonDiv.style.display = 'inline';
+  submenuButtonDiv.onclick = (function() {
+    return function() {
+      selfMap.clearData();
+      return false;
+    };
+  })();
+  rightHeaderMenu.appendChild(submenuButtonDiv);
+
+  this.divBelt.appendChild(rightHeaderMenu);
+};
+
+CustomMap.prototype.registerSource = function(overlayCollection) {
+  this.overlayCollections[overlayCollection.name] = overlayCollection;
+  overlayCollection.aliasMarkers = [];
+  overlayCollection.pointMarkers = [];
+  overlayCollection.reactionMarkers = [];
+};
+
+CustomMap.prototype.refreshComments = function() {
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)
+        && overlayName == "comment") {
+      var collection = this.overlayCollections[overlayName];
+      collection.refresh();
+      return;
+    }
+  }
+  throw "comment OverlayCollection not found";
+};
+
+CustomMap.prototype.turnOnOffDrawing = function() {
+  var model = this.getSubmodelById(this.getActiveSubmapId());
+  if (model != null) {
+    model._turnOnOffDrawing();
+  } else {
+    throw "Cannot find submodel with id: " + this.getActiveSubmapId();
+  }
+};
+
+CustomMap.prototype.clearData = function() {
+  this.clearOverlays();
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      ServerConnector.sendClearRequest(overlayName);
+    }
+  }
+};
+
+CustomMap.prototype.refreshMarkers = function() {
+  logger.debug("Refresh Markers: ");
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      var collection = this.overlayCollections[overlayName];
+      this.refreshOverlayMarkers(collection);
+    }
+  }
+};
+
+CustomMap.prototype.refreshOverlayMarkers = function(overlay) {
+  var self = this;
+  logger.debug("Refresh overlay: " + overlay.name);
+  var boundsArray = [];
+  boundsArray[this.getId()] = new google.maps.LatLngBounds();
+  for (var i = 0; i < this.submaps.length; i++) {
+    boundsArray[this.submaps[i].getId()] = new google.maps.LatLngBounds();
+  }
+
+  var updated = false;
+  var stillMissing = false;
+  for ( var key in overlay.aliasMarkers) {
+    if (overlay.aliasMarkers.hasOwnProperty(key)
+        && overlay.aliasMarkers[key] != null) {
+      var alias = overlay.aliasMarkers[key];
+      if (alias.getAliasData() == null) {
+        var aliasData = alias.getCustomMap().mapModel.getAliasById(alias
+            .getId());
+        if (aliasData != null) {
+          alias.setAliasData(aliasData);
+          alias.init();
+          alias.show();
+          updated = true;
+          var bounds = alias.getBounds();
+          boundsArray[alias.getCustomMap().getId()].extend(bounds
+              .getNorthEast());
+        } else {
+          stillMissing = true;
+          logger.debug("Cannot show alias marker. Data is still not loaded...");
+        }
+      } else {
+        var bounds = alias.getBounds();
+        if (!this.isMarkerOptimization()) {
+          alias.hide();
+          alias.show();
+        }
+        boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
+        boundsArray[alias.getCustomMap().getId()].extend(bounds.getSouthWest());
+      }
+    }
+  }
+
+  for ( var key in overlay.pointMarkers) {
+    if (overlay.pointMarkers.hasOwnProperty(key)
+        && overlay.pointMarkers[key] != null) {
+      var alias = overlay.pointMarkers[key];
+      // we don't need to update this markers because thet data about
+      // visualization is
+      // already there
+      // alias.update();
+      var bounds = alias.getBounds();
+      if (!this.isMarkerOptimization()) {
+        alias.hide();
+        alias.show();
+      }
+      boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
+    }
+  }
+
+  for ( var key in overlay.reactionMarkers) {
+    if (overlay.reactionMarkers.hasOwnProperty(key)
+        && overlay.reactionMarkers[key] != null) {
+      var reactionOverlay = overlay.reactionMarkers[key];
+      if (reactionOverlay.getReactionData() == null) {
+        var reactionData = reactionOverlay.getCustomMap().mapModel
+            .getReactionById(reactionOverlay.getId());
+        if (reactionData != null) {
+          reactionOverlay.setReactionData(reactionData);
+          reactionOverlay.init();
+          reactionOverlay.show();
+          updated = true;
+          var bounds = reactionOverlay.getBounds();
+          boundsArray[reactionOverlay.getCustomMap().getId()].extend(bounds
+              .getNorthEast());
+          boundsArray[reactionOverlay.getCustomMap().getId()].extend(bounds
+              .getSouthWest());
+        } else {
+          stillMissing = true;
+          logger
+              .debug("Cannot show reaction marker. Data is still not loaded...");
+        }
+      } else {
+        var bounds = alias.getBounds();
+        if (!this.isMarkerOptimization()) {
+          alias.hide();
+          alias.show();
+        }
+        boundsArray[alias.getCustomMap().getId()].extend(bounds.getNorthEast());
+        boundsArray[alias.getCustomMap().getId()].extend(bounds.getSouthWest());
+      }
+
+    }
+  }
+
+  if (!stillMissing && updated && overlay.fitBounds) {
+    for ( var mapId in boundsArray) {
+      if (boundsArray.hasOwnProperty(mapId)) {
+        var map = this.getSubmodelById(mapId).map;
+        var bounds = boundsArray[mapId];
+        if (map != null && !bounds.isEmpty()) {
+          if (typeof map.fitBounds2 !== "undefined") {
+            map.fitBounds2(bounds);
+          } else {
+            map.fitBounds(bounds);
+          }
+        }
+      }
+    }
+  }
+};
+
+CustomMap.prototype.openSubmodel = function(id, htmlTag, jsVar) {
+  if (jsVar.submapControler == null) {
+    var submap = null;
+    for (var i = 0; i < this.submaps.length; i++) {
+      if (this.submaps[i].getId() == id) {
+        submap = this.submaps[i];
+      }
+    }
+    if (submap == null) {
+      logger.error("Unknown submap for id: " + id);
+    } else {
+      submap.init(htmlTag, jsVar);
+      //we have to perform it on top map, because on submaps id is different
+      this.openLayout(this.map.getMapTypeId());
+	    
+      this.refreshOverlays();
+
+      // now we have to visualize layouts
+      var layouts = [];
+
+      // get list of layouts
+      for ( var key in this.selectedLayouts) {
+        if (this.selectedLayouts.hasOwnProperty(key)
+            && this.selectedLayouts[key] == true) {
+          layouts.push(key);
+        }
+      }
+
+      // show layouts that should be visualized (resize or show them)
+      for (var i = 0; i < layouts.length; i++) {
+        var layoutId = layouts[i];
+        submap._showSelectedLayout(layoutId, i, layouts.length);
+      }
+    }
+  }
+  jsVar.show();
+
+};
+
+CustomMap.prototype.customizeGoogleMapView = function() {
+  var mapOptions = this.creatMapOptions();
+  this.map.setOptions(mapOptions);
+
+  this.createMapMenu(false);
+
+  this.registerMapClickEvents();
+
+  this.createLogo();
+  // this.createMapVersion();
+  google.maps.event.trigger(this.map, 'resize');
+  google.maps.event.trigger(this.map, 'maptypeid_changed');
+  google.maps.event.trigger(this.map, 'projection_changed');
+
+  // center map and zoom in to fit into browser window
+  if (this.getConfiguration().fitMapBounds) {
+    var bounds = new google.maps.LatLngBounds();
+    var point = new google.maps.LatLng(
+        this.getConfiguration().topLeftLatLng.lat,
+        this.getConfiguration().topLeftLatLng.lng);
+    bounds.extend(point);
+
+    point = new google.maps.LatLng(
+        this.getConfiguration().bottomRightLatLng.lat,
+        this.getConfiguration().bottomRightLatLng.lng);
+    bounds.extend(point);
+
+    if (typeof this.map.fitBounds2 !== "undefined") {
+      this.map.fitBounds2(bounds);
+    } else {
+      this.map.fitBounds(bounds);
+    }
+  }
+};
+
+CustomMap.prototype.setCenter = function(mapIdentifier, coordinates) {
+  if (this.getConfiguration().ID_MODEL == mapIdentifier) {
+    this.map.setCenter(coordinates);
+  } else {
+    GuiConnector.openDialog(mapIdentifier);
+    for (var i = 0; i < this.submaps.length; i++) {
+      if (this.submaps[i].getId() == mapIdentifier) {
+	if (coordinates instanceof google.maps.Point) {
+		coordinates = this.submaps[i].fromPointToLatLng(coordinates);
+	}
+        this.submaps[i].map.setCenter(coordinates);
+      }
+    }
+  }
+};
+
+CustomMap.prototype.setZoom = function(mapIdentifier, zoom) {
+  if (this.getConfiguration().ID_MODEL == mapIdentifier) {
+    this.map.setZoom(zoom);
+  } else {
+    GuiConnector.openDialog(mapIdentifier);
+    for (var i = 0; i < this.submaps.length; i++) {
+      if (this.submaps[i].getId() == mapIdentifier) {
+        this.submaps[i].map.setZoom(zoom);
+      }
+    }
+  }
+};
+
+/**
+ * Creates listeners for google.maps.Map object that will actualize the data in
+ * user session.
+ */
+CustomMap.prototype.createMapChangedCallbacks = function() {
+  var customMapSelf = this;
+  // listener for changing zoom level
+  google.maps.event.addListener(this.map, 'zoom_changed', function() {
+    ServerConnector.setZoomLevel(customMapSelf.map.getZoom());
+    ServerConnector.actualizeSessionData();
+  });
+
+  // if we have zoom level data stored in session then restore it
+  var level = ServerConnector.getZoomLevel();
+  if (parseInt(level) > 0) {
+    level = parseInt(level);
+    this.map.setZoom(level);
+  } else {
+    ServerConnector.setZoomLevel(customMapSelf.map.getZoom());
+  }
+
+  // listener for changing location of the map (moving left/reght/top/bottom
+  google.maps.event.addListener(this.map, 'center_changed', function() {
+    var coord = customMapSelf.map.getCenter();
+    var point = customMapSelf.fromLatLngToPoint(coord);
+    ServerConnector.setCenterCoordinateX(point.x);
+    ServerConnector.setCenterCoordinateY(point.y);
+    ServerConnector.actualizeSessionData();
+  });
+
+  // if we have coordinate data stored in session then restore it
+  var x = ServerConnector.getCenterCoordinateX();
+  var y = ServerConnector.getCenterCoordinateY();
+  if (!isNaN(x) && !isNaN(y)) {
+    var point = new google.maps.Point(x, y);
+    var coord = customMapSelf.fromPointToLatLng(point);
+    customMapSelf.map.setCenter(coord);
+  }
+
+  // listener for changing type of layout
+  google.maps.event.addListener(this.map, 'maptypeid_changed', function() {
+    ServerConnector.setSelectedLayout(customMapSelf.map.getMapTypeId());
+    ServerConnector.actualizeParams();
+  });
+
+  // if we have type of layout stored in the session then restore it
+  var mapType = ServerConnector.getSelectedLayout();
+  if (mapType != "" && mapType != null) {
+    this.openLayout(mapType);
+  }
+};
+
+/**
+ * Returns submodel (or this model) by identfier of the model.
+ * 
+ * @param identifier
+ *            identifier of the submodel
+ * @returns submodel (or this model) with given identfier of the model
+ */
+CustomMap.prototype.getSubmodelById = function(identifier) {
+  if (this.getId() == identifier) {
+    return this;
+  }
+  for (var i = 0; i < this.submaps.length; i++) {
+    if (this.submaps[i].getId() == identifier) {
+      return this.submaps[i];
+    }
+  }
+  return null;
+};
+
+CustomMap.prototype.removeSelection = function() {
+  var model = this.getSubmodelById(this.getActiveSubmapId());
+  if (model != null) {
+    model._removeSelection();
+  } else {
+    throw "Cannot find submodel with id: " + this.getActiveSubmapId();
+  }
+};
+
+/**
+ * This method will hide google map view and will present single image overview
+ * of the data.
+ */
+CustomMap.prototype.showOverview = function(overviewImageId) {
+  overviewDialog.syncWindowResize();
+  if (this.getOverviewDiv() == "undefined" || this.getOverviewDiv() == null) {
+    logger.warn("Cannot show overview, because overview div is  undefined");
+  } else {
+    logger.debug("Show overview");
+    overviewDialog.show();
+
+    // resize dialog
+    var htmlTag = GuiConnector.getOverviewHtmlTag();
+
+    var width = Math.floor(window.innerWidth * 2 / 3);
+    var height = Math.floor(window.innerHeight * 2 / 3);
+
+    htmlTag.style.height = (height + 50) + "px";
+    htmlTag.style.width = (width + 20) + "px";
+
+    var self = this;
+
+    // remove all child nodes from overview div
+    while (this.getOverviewDiv().hasChildNodes()) {
+      this.getOverviewDiv().removeChild(this.getOverviewDiv().lastChild);
+    }
+
+    if (overviewImageId == "undefined" || overviewImageId == null) {
+      this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
+    } else {
+      this.overviewImage = null;
+      for (var i = 0; i < this.getConfiguration().OVERVIEW_IMAGES.length; i++) {
+        if (this.getConfiguration().OVERVIEW_IMAGES[i].idObject == overviewImageId) {
+          this.overviewImage = this.getConfiguration().OVERVIEW_IMAGES[i];
+        }
+      }
+
+      if (this.overviewImage == null) {
+        logger.warn("Unknown overview image with id = " + overviewImageId);
+        this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
+      }
+    }
+
+    // add image to overview div
+    this.overviewImageTag = document.createElement("IMG");
+    this.overviewImageTag.src = "../map_images/" + this.overviewImage.filename;
+    this.getOverviewDiv().appendChild(this.overviewImageTag);
+
+    var ratio = 1.0;
+
+    // check how image should be resized to fit dialog and resize it manually!!!
+    if (width / this.overviewImage.width > height / this.overviewImage.height) {
+      this.overviewImageTag.style.height = height + "px";
+      ratio = height / this.overviewImage.height;
+      width = this.overviewImage.width * ratio;
+
+      htmlTag.style.width = (width + 20) + "px";
+    } else {
+      this.overviewImageTag.style.width = width + "px";
+      ratio = width / this.overviewImage.width;
+      height = this.overviewImage.height * ratio;
+
+      htmlTag.style.height = (height + 50) + "px";
+    }
+
+    // center dialog
+    overviewDialog.jq.css("top", Math.max(0,
+        (($(window).height() - overviewDialog.jq.outerHeight()) / 2)
+            + $(window).scrollTop())
+        + "px");
+    overviewDialog.jq.css("left", Math.max(0,
+        (($(window).width() - overviewDialog.jq.outerWidth()) / 2)
+            + $(window).scrollLeft())
+        + "px");
+
+    // on click event (what should happen when we click on the image)
+    var onclickevent = function getClickPosition(e) {
+      var parentPosition = getPosition(e.currentTarget);
+      var xPosition = e.clientX - parentPosition.x;
+      var yPosition = e.clientY - parentPosition.y;
+
+      var imgWidth = self.overviewImageTag.offsetWidth;
+
+      var currentRatio = imgWidth / self.overviewImage.width;
+
+      var xNormal = xPosition / currentRatio;
+      var yNormal = yPosition / currentRatio;
+      var point = {
+        x : xNormal,
+        y : yNormal
+      };
+
+      var link = null;
+      for (var i = 0; i < self.overviewImage.links.length; i++) {
+        if (pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
+          if (link == null) {
+            link = self.overviewImage.links[i];
+          } else {
+            logger.warn("More than one link found. Skipping");
+          }
+        }
+      }
+      if (link != null) {
+        if (link.type == "OverviewModelLink") {
+          logger.debug("Opening model from overview. ModelId: "
+              + link.modelLinkId);
+          logger.debug("link coordinates [" + link.idObject + "]: "
+              + link.latLng);
+          // TODO min zoom value can be different for every map, it should be
+          // changed in the future
+          self.showModel(link.modelLinkId, link.latLng, link.zoomLevel
+              + self.getConfiguration().MIN_ZOOM);
+          overviewDialog.hide();
+        } else if (link.type == "OverviewImageLink") {
+          logger.debug("Opening image from overview. ImageId: "
+              + link.imageLinkId);
+          self.showOverview(link.imageLinkId);
+        } else if (link.type == "OverviewSearchLink") {
+          logger.debug("Sending search query. Query: " + link.query);
+          searchPanel.search(link.query);
+          overviewDialog.hide();
+        } else {
+          logger.warn("Unknown type of link: " + link.type
+              + ". Don't know what to do... LinkId: " + link.idObject);
+        }
+      }
+    };
+
+    this.overviewImageTag.onclick = onclickevent;
+
+    // resize canvas where on mouse over highligh will appear
+    var canvas = document.getElementById("canvasDebug");
+    canvas.width = width;
+    canvas.height = height;
+    canvas.onclick = onclickevent;
+
+    // in debug mode draw clickable shapes
+    if (DEBUG_ON) {
+      var ctx = canvas.getContext("2d");
+      // clear canvas
+      ctx.clearRect(0, 0, canvas.width, canvas.height);
+      for (var i = 0; i < this.overviewImage.links.length; i++) {
+        ctx.beginPath();
+        var polygon = this.overviewImage.links[i].polygon;
+        for (var j = 0; j < polygon.length; j++) {
+          var x = polygon[j].x * ratio;
+          var y = polygon[j].y * ratio;
+          ctx.moveTo(x, y);
+          x = polygon[(j + 1) % polygon.length].x * ratio;
+          y = polygon[(j + 1) % polygon.length].y * ratio;
+          ctx.lineTo(x, y);
+        }
+        ctx.stroke();
+      }
+    }
+
+    this.overviewImage.mousePos = {
+      x : 0,
+      y : 0
+    };
+
+    // this listener should be called when mouse moves over image, it purpose is
+    // to change coursor to pointer when mouse enters clickable polygon and back
+    // to normal when mouse leaves such region
+    var onmousemove = function getMouseOverPosition(e) {
+      var position = getPosition(e.currentTarget);
+      position.x = e.clientX - position.x;
+      position.y = e.clientY - position.y;
+
+      var imgWidth = self.overviewImageTag.offsetWidth;
+
+      var currentRatio = imgWidth / self.overviewImage.width;
+
+      var xNormal = position.x / currentRatio;
+      var yNormal = position.y / currentRatio;
+      var point = {
+        x : xNormal,
+        y : yNormal
+      };
+
+      if (self.overviewImage.mousePos.x != position.x
+          || self.overviewImage.mousePos.y != position.y) {
+        self.overviewImage.mousePos = position;
+        var link = null;
+        for (var i = 0; i < self.overviewImage.links.length; i++) {
+          if (pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
+            link = self.overviewImage.links[i];
+          }
+        }
+        if (link == null) {
+          e.currentTarget.style.cursor = "auto";
+        } else {
+          e.currentTarget.style.cursor = "pointer";
+        }
+      }
+    };
+
+    // onmousemove listener should be assigned to canvas (which is on top of the
+    // image) and overviewimage (just in case something went wrong with resizing
+    // canvas)
+    canvas.onmousemove = onmousemove;
+    this.overviewImageTag.onmousemove = onmousemove;
+  }
+};
+
+CustomMap.prototype.showModel = function(id, point, zoomLevel) {
+  if (point != "undefined") {
+    this.setCenter(id, point);
+  } else {
+    logger.warn("Center point undefined...");
+  }
+  if (zoomLevel != "undefined") {
+    this.setZoom(id, zoomLevel);
+  } else {
+    logger.warn("Zoom level undefined...");
+  }
+};
+
+/**
+ * Adds information about aliases visible in the specific layout.
+ * 
+ * @param layoutId
+ *            identifier of the layout
+ * 
+ * @param jsonAliases
+ *            list of aliases in json format
+ * 
+ */
+CustomMap.prototype.addAliasesForLayout = function(layoutId, jsonAliases) {
+  logger.debug("Adding aliases for layout: " + layoutId);
+
+  // init layout data
+  if (this.mapModel.getLayoutDataById(layoutId) == null) {
+    this.mapModel.initLayoutData(layoutId);
+    for (var i = 0; i < this.submaps.length; i++) {
+      this.submaps[i].mapModel.initLayoutData(layoutId);
+    }
+  }
+
+  var aliases = JSON.parse(jsonAliases);
+  for (var i = 0; i < aliases.length; i++) {
+    var alias = aliases[i];
+    var model = this.getSubmodelById(alias.modelId);
+    if (model != 'undefined' && model != null) {
+      model.mapModel.addAliasForLayout(layoutId, alias);
+    } else {
+      logger.warn("Unknown model: " + alias.modelId);
+    }
+  }
+
+  this.retrieveMissingAliases();
+}
+
+/**
+ * Adds information about aliases.
+ * 
+ * @param jsonAliases
+ *            list of aliases in json format
+ * 
+ */
+CustomMap.prototype.addAliases = function(aliases) {
+  for (var i = 0; i < aliases.length; i++) {
+    var alias = aliases[i];
+    var model = this.getSubmodelById(alias.modelId);
+    if (model != 'undefined' && model != null) {
+      model.addAlias(alias);
+    } else {
+      logger.warn("Unknown model: " + alias.modelId);
+    }
+  }
+  this.callListeners("onAddAliases");
+}
+
+/**
+ * This function will ask server for aliases that should be visualized but the
+ * data is still missing on the client side.
+ */
+CustomMap.prototype.retrieveMissingAliases = function() {
+  var ids = [];
+  var missing = this.mapModel.getMissingAliasIds();
+  for (var j = 0; j < missing.length; j++) {
+    ids.push([ this.getId(), missing[j] ]);
+  }
+  for (var i = 0; i < this.submaps.length; i++) {
+    missing = this.submaps[i].mapModel.getMissingAliasIds();
+    for (var j = 0; j < missing.length; j++) {
+      ids.push([ this.submaps[i].getId(), missing[j] ]);
+    }
+  }
+  if (ids.length > 0) {
+    // load data from server about missing aliases
+    ServerConnector.retreiveLightAliases(ids);
+  }
+  if (!ServerConnector.isWaitingForData()) {
+    // if we already have everything then just refresh data to be visualized
+    this.refreshSelectedLayouts();
+    //and close "loading" dialog
+    GuiConnector.closeLoadingDialog();
+  }
+}
+
+/**
+ * Adds layout to be visualized.
+ * 
+ * @param identifier
+ *            identifier of the layout that should be included in visualization
+ */
+CustomMap.prototype.addSelectedLayout = function(identifier, name) {
+  logger.debug("Selecting layout: " + identifier);
+
+  if (this.selectedLayouts[identifier] == true) {
+    logger.warn("Layout " + identifier + " already selected");
+  } else {
+    this.selectedLayouts[identifier] = true;
+
+    // open dialog with info that we are loading data (it takes some time for
+    // bigger layouts on big maps)
+    GuiConnector.openLoadingDialog();
+
+    // if we don't have information about this layout then download it
+    if (this.mapModel.getLayoutDataById(identifier) == null) {
+      // initialize what we can on client side
+      this.mapModel.initLayoutData(identifier, name);
+      for (var i = 0; i < this.submaps.length; i++) {
+        this.submaps[i].mapModel.initLayoutData(identifier, name);
+      }
+
+      // load data from server about this layout
+      ServerConnector.retreiveActiveAliasesForLayout(identifier);
+
+      // load data from server about this layout
+      ServerConnector.retreiveActiveReactionsForLayout(identifier);
+    }
+    if (!ServerConnector.isWaitingForData()) {
+      // if we already loaded the data then just visualize it
+      this.refreshSelectedLayouts();
+      //and close "loading" dialog (if opened)
+      GuiConnector.closeLoadingDialog();
+    }
+    // if we have to load data from server then open info window should be
+    // opened
+    ServerConnector
+        .setVisibleLayouts(JSON.stringify(this.getSelectedLayouts()));
+
+  }
+};
+
+/**
+ * Returns list of layouts that are selected and visualized by javascript.
+ * 
+ * @return array with a list of selected layotus
+ * 
+ */
+CustomMap.prototype.getSelectedLayouts = function() {
+  var layouts = [];
+
+  // get list of layouts
+  for ( var key in this.selectedLayouts) {
+    if (this.selectedLayouts.hasOwnProperty(key)
+        && this.selectedLayouts[key] == true) {
+      layouts.push(key);
+    }
+  }
+  return layouts;
+}
+
+/**
+ * Removes layout from visualization.
+ * 
+ * @param identifier
+ *            identifier of layout to remove
+ * 
+ */
+CustomMap.prototype.removeSelectedLayout = function(identifier) {
+  logger.debug("Removing layout: " + identifier);
+
+  if (this.selectedLayouts[identifier] != true) {
+    logger.warn("Layout " + identifier + " is not selected");
+  } else {
+    this.selectedLayouts[identifier] = false;
+    this.refreshSelectedLayouts();
+    ServerConnector
+        .setVisibleLayouts(JSON.stringify(this.getSelectedLayouts()));
+  }
+};
+
+/**
+ * Refresh visualization of selected layouts.
+ */
+CustomMap.prototype.refreshSelectedLayouts = function() {
+  logger.debug("Refreshing layouts");
+  var layouts = this.getSelectedLayouts();
+
+  // show layouts that should be visualized (resize or show them)
+  for (var i = 0; i < layouts.length; i++) {
+    var layoutId = layouts[i];
+    if (this.layoutContainsOverlays(layoutId)) {
+      // resize element on the map
+      this.resizeSelectedLayout(layoutId, i, layouts.length);
+    } else {
+      this.showSelectedLayout(layoutId, i, layouts.length);
+    }
+  }
+
+  // remove layouts that were
+  for ( var key in this.selectedLayoutOverlays) {
+    if (!this.selectedLayouts.hasOwnProperty(key)
+        || this.selectedLayouts[key] == false) {
+      if (this.layoutContainsOverlays(key)) {
+        this.hideSelectedLayout(key);
+      }
+    }
+  }
+  this.refreshInfoWindows();
+};
+
+/**
+ * Hides layout from the map and all submaps
+ * 
+ * @param layoutId
+ *            identifier of a layout to hide
+ */
+CustomMap.prototype.hideSelectedLayout = function(layoutId) {
+  this._hideSelectedLayout(layoutId);
+  for (var i = 0; i < this.submaps.length; i++) {
+    this.submaps[i]._hideSelectedLayout(layoutId);
+  }
+}
+
+/**
+ * Resize(refresh) layout on the map and all submaps. Resizing should be called
+ * when number of layouts to visualize change.
+ * 
+ * @param layoutId
+ *            identifier of layout to refresh
+ * @param index
+ *            position of the layout in list of layouts that we visualize
+ * @param length
+ *            number of layouts that we currently visualize
+ */
+CustomMap.prototype.resizeSelectedLayout = function(layoutId, index, length) {
+  logger.debug("Resize layout: " + layoutId);
+  this._resizeSelectedLayout(layoutId, index, length);
+  for (var i = 0; i < this.submaps.length; i++) {
+    this.submaps[i]._resizeSelectedLayout(layoutId, index, length);
+  }
+}
+
+/**
+ * Show layout on the map and all submaps.
+ * 
+ * @param layoutId
+ *            identifier of layout to show
+ * @param index
+ *            position of the layout in list of layouts that we visualize
+ * @param length
+ *            number of layouts that we currently visualize
+ */
+CustomMap.prototype.showSelectedLayout = function(layoutId, index, length) {
+  logger.debug("Resize layout: " + layoutId);
+  this._showSelectedLayout(layoutId, index, length);
+  for (var i = 0; i < this.submaps.length; i++) {
+    this.submaps[i]._showSelectedLayout(layoutId, index, length);
+  }
+}
+
+/**
+ * Adds information about reactions visible in the specific layout.
+ * 
+ * @param layoutId
+ *            identifier of the layout
+ * 
+ * @param jsonAliases
+ *            list of reactions in json format
+ * 
+ */
+CustomMap.prototype.addReactionsForLayout = function(layoutId, jsonReactions) {
+  logger.debug("Adding reactions for layout: " + layoutId);
+  var reactions = JSON.parse(jsonReactions);
+  for (var i = 0; i < reactions.length; i++) {
+    var reaction = reactions[i];
+    var model = this.getSubmodelById(reaction.modelId);
+    if (model != 'undefined' && model != null) {
+      model.mapModel.addReactionForLayout(layoutId, reaction);
+    } else {
+      logger.warn("Unknown model: " + reaction.modelId);
+    }
+  }
+  this.retrieveMissingReactions();
+}
+
+/**
+ * Adds information about reactions.
+ * 
+ * @param jsonAliases
+ *            list of reactions in json format
+ * 
+ */
+CustomMap.prototype.addReactions = function(jsonReactions) {
+  var reactions = JSON.parse(jsonReactions);
+  for (var i = 0; i < reactions.length; i++) {
+    var reaction = reactions[i];
+    var model = this.getSubmodelById(reaction.modelId);
+    if (model != 'undefined' && model != null) {
+      model.addReaction(reaction);
+    } else {
+      logger.warn("Unknown model: " + reaction.modelId);
+    }
+  }
+  this.callListeners("onAddReactions");
+}
+
+/**
+ * This function will ask server for reactions that should be visualized but the
+ * data is still missing on the client side.
+ */
+CustomMap.prototype.retrieveMissingReactions = function() {
+  var ids = [];
+  var missing = this.mapModel.getMissingReactionIds();
+  for (var j = 0; j < missing.length; j++) {
+    ids.push([ this.getId(), missing[j] ]);
+  }
+  for (var i = 0; i < this.submaps.length; i++) {
+    missing = this.submaps[i].mapModel.getMissingReactionIds();
+    for (var j = 0; j < missing.length; j++) {
+      ids.push([ this.submaps[i].getId(), missing[j] ]);
+    }
+  }
+  if (ids.length > 0) {
+    // load data from server about missing reactions
+    ServerConnector.retreiveLightReactions(ids);
+  }
+  if (!ServerConnector.isWaitingForData()) {
+    // if we already have everything then just refresh data to be visualized
+    this.refreshSelectedLayouts();
+    //and close "loading" dialog (if opened)
+    GuiConnector.closeLoadingDialog();
+  }
+}
+
+/**
+ * This method checks if the layout contains any overlays (like AliasOverlay or
+ * ReactionOverlay) that is currently visible on the map.
+ * 
+ * @param layoutId
+ *            identifier of the layout
+ * @returns {Boolean}: <code>true</code> if the layout contains overlays to
+ *          visualize, <code>false</code> otherwise
+ */
+CustomMap.prototype.layoutContainsOverlays = function(layoutId) {
+
+  // first, check top map
+  if (this.selectedLayoutOverlays.hasOwnProperty(layoutId)
+      && this.selectedLayoutOverlays[layoutId].length > 0) {
+    return true;
+  }
+
+  // now check all submaps
+  for (var i = 0; i < this.submaps.length; i++) {
+    if (this.submaps[i].initialized) {
+      if (this.submaps[i].selectedLayoutOverlays.hasOwnProperty(layoutId)
+          && this.submaps[i].selectedLayoutOverlays[layoutId].length > 0) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/**
+ * Refresh content of all {@link AliasInfoWindow} in this map and all submaps.
+ */
+CustomMap.prototype.refreshInfoWindows = function() {
+  this._refreshInfoWindows();
+  // now check all submaps
+  for (var i = 0; i < this.submaps.length; i++) {
+    this.submaps[i]._refreshInfoWindows();
+  }
+}
+
+/**
+ * Opens {@link AliasInfoWindow} for an {@link Alias} in a given model/submodel.
+ * 
+ * @param aliasId
+ *            identifier of {@link Alias}
+ * @param modelId
+ *            identifier of {@link AbstractCustomMap}
+ */
+CustomMap.prototype.openInfoWindowForAlias = function(aliasId, modelId) {
+  logger.debug("Opening info window for alias: " + aliasId + ", model: "
+      + modelId);
+  var model = this.getSubmodelById(modelId);
+  var alias = model.mapModel.getAliasById(aliasId);
+
+  // if we have only simple version of the data then ask server for more details
+  if (alias == null || alias.completness == 'SIMPLE') {
+    logger.debug("Accessing full alias: " + aliasId);
+    var ids = [ [ modelId, aliasId ] ];
+    ServerConnector.retreiveFullAliases(ids);
+  }
+  // open AliasInfoWindow in a right model
+  model._openInfoWindowForAlias(aliasId);
+};
+
+/**
+ * Renders markers, lines, etc. for elements highlighted in OverlayCollection.
+ * 
+ * @param overlayCollection
+ *            {@link OverlayCollection} to be processed
+ * @param fitBounds
+ *            <code>true</code> if the borders should fit bounds after
+ *            creating all elements
+ */
+CustomMap.prototype.renderOverlayCollection = function(overlayCollection,
+    fitBounds) {
+  var elements = overlayCollection.elements;
+  var missingElements = false;
+
+  var boundsArray = [];
+  boundsArray[this.getId()] = new google.maps.LatLngBounds();
+  for (var i = 0; i < this.submaps.length; i++) {
+    boundsArray[this.submaps[i].getId()] = new google.maps.LatLngBounds();
+  }
+
+  for (var i = 0; i < elements.length; i++) {
+    var element = elements[i];
+    var model = this.getSubmodelById(element.modelId);
+    if (element.type == "ALIAS") {
+      if (overlayCollection.aliasMarkers[element.getId()] != null) {
+        logger.warn("More than one marker in " + overlayCollection.name
+            + " for alias " + element.getId() + ". Skipping duplicates.");
+      } else {
+        var aliasData = model.mapModel.getAliasById(element.getId());
+        if (aliasData == null) {
+          model.mapModel.addMissingAliasId(element.getId());
+          missingElements = true;
+        }
+        var marker = new AliasMarker(element.getId(), element.icon, aliasData,
+            model);
+        overlayCollection.aliasMarkers[element.getId()] = marker;
+        if (!missingElements) {
+          var bounds = marker.getBounds();
+          boundsArray[element.modelId].extend(bounds.getNorthEast());
+          boundsArray[element.modelId].extend(bounds.getSouthWest());
+        }
+      }
+    } else if (element.type == "REACTION") {
+      var reactionData = model.mapModel.getReactionById(element.getId());
+      if (reactionData == null) {
+        model.mapModel.addMissingReactionId(element.getId());
+        missingElements = true;
+      }
+      var marker = null;
+      var icon = element.getIcon();
+
+      if (icon === null || icon === undefined) {
+        // this should happen when we visualize search data (there is
+        // no marker, but only flat overlay of the reaction lines)
+        //
+        marker = new ReactionOverlay(null, reactionData, model, false, element
+            .getId());
+      } else {
+        // when we have icon defined (for instance when it comes from
+        // comment) then we don't want to have overlayed reaction lines
+        // but icon marker
+        marker = new ReactionMarker(element.getId(), icon, reactionData, model);
+      }
+      overlayCollection.reactionMarkers[element.getId()] = marker;
+      if (!missingElements) {
+        var bounds = marker.getBounds();
+        boundsArray[element.modelId].extend(bounds.getNorthEast());
+        boundsArray[element.modelId].extend(bounds.getSouthWest());
+
+      }
+    } else if (element.type == "POINT") {
+      var pointData = model.mapModel.getPointDataByPoint(element.getPoint());
+      var marker = new PointMarker(pointData, element.icon, model);
+      overlayCollection.pointMarkers[pointData.getId()] = marker;
+      if (!missingElements) {
+        var bounds = marker.getBounds();
+        boundsArray[element.modelId].extend(bounds.getNorthEast());
+        boundsArray[element.modelId].extend(bounds.getSouthWest());
+      }
+    } else {
+      logger.warn("Unknown type of the element in overlay: " + element.type);
+    }
+    var infoWindow = this.getInfoWindowForIdentifiedElement(element);
+    if (infoWindow != null) {
+      this.retrieveOverlayDetailDataForElement(element, infoWindow
+          .getOverlayFullViewArray());
+      this.updateInfoWindowForIdentifiedElement(element);
+    }
+  }
+
+  if (missingElements) {
+    this.retrieveMissingReactions();
+    this.retrieveMissingAliases();
+  } else {
+    if (elements.length > 0 && fitBounds) {
+      for ( var mapId in boundsArray) {
+        if (boundsArray.hasOwnProperty(mapId)) {
+          var map = this.getSubmodelById(mapId).map;
+          var bounds = boundsArray[mapId];
+          if (map != null && !bounds.isEmpty()) {
+            if (typeof map.fitBounds2 !== "undefined") {
+              map.fitBounds2(bounds);
+            } else {
+              map.fitBounds(bounds);
+            }
+          }
+        }
+      }
+    }
+  }
+};
+
+/**
+ * Creates and register listeners to be called on events:
+ * <ul>
+ * <li>onAddAliases</li>
+ * <li>onAddReactions</li>
+ * </ul>
+ */
+CustomMap.prototype.createClientServerListeners = function() {
+  this.registerListenerType("onAddAliases");
+  this.registerListenerType("onAddReactions");
+
+  var refreshLayoutsFun = function(e) {
+    var self = e.object;
+    if (!ServerConnector.isWaitingForData()) {
+      self.refreshSelectedLayouts();
+      //and close "loading" dialog (if opened)
+      GuiConnector.closeLoadingDialog();
+    }
+  };
+
+  var refreshOverlaysFun = function(e) {
+    e.object.refreshMarkers();
+  };
+
+  this.addListener("onAddAliases", refreshLayoutsFun);
+  this.addListener("onAddAliases", refreshOverlaysFun);
+
+  this.addListener("onAddReactions", refreshLayoutsFun);
+  this.addListener("onAddReactions", refreshOverlaysFun);
+
+};
+
+/**
+ * Opens {@link AbstractInfoWindow} for a marker.
+ * 
+ * @param marker
+ *            {@link AbstractMarker} for which info window should be opened
+ */
+CustomMap.prototype.openInfoWindowForMarker = function(marker) {
+  var modelId = marker.getCustomMap().getId();
+  var markerId = marker.getId();
+  var model = this.getSubmodelById(modelId);
+  logger.debug("Opening info window for " + marker.constructor.name + ": "
+      + markerId + ", model: " + modelId);
+  var elementType = marker.getType();
+  if (marker instanceof AliasMarker) {
+    var alias = model.mapModel.getAliasById(markerId);
+
+    // if we have only simple version of the data then ask server for more
+    // details
+    if (alias == null || alias.completness == 'SIMPLE') {
+      logger.debug("Accessing full alias: " + markerId);
+      var ids = [ [ modelId, markerId ] ];
+      ServerConnector.retreiveFullAliases(ids);
+    }
+  } else if (marker instanceof PointMarker) {
+    // no special treatment for points
+  } else if (marker instanceof ReactionMarker) {
+    // no special treatment for reactions
+  } else {
+    logger.error("Unknown marker type: " + marker.constructor.name);
+  }
+
+  // open AliasInfoWindow in a right model
+  model._openInfoWindowForMarker(marker);
+
+  var infoWindow = model.returnInfoWindowForMarker(marker);
+
+  var element = new IdentifiedElement({
+    objectId : markerId,
+    modelId : modelId,
+    type : elementType
+  });
+
+  this.retrieveOverlayDetailDataForElement(element, infoWindow
+      .getOverlayFullViewArray());
+
+};
+
+/**
+ * Sends requestes to download detailed data about element in all
+ * {@link OverlayCollection}.
+ * 
+ * @param element
+ *            element for which we want to have detailed information
+ */
+CustomMap.prototype.retrieveOverlayDetailDataForElement = function(element,
+    general) {
+  if (general === undefined) {
+    logger.warn("general param is undefined!");
+    general = [];
+  }
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      var overlay = this.overlayCollections[overlayName];
+
+      var generalRequest = general[overlayName];
+      if (generalRequest === undefined) {
+        logger.warn(
+            "No information about general overlay request for overlay: ",
+            overlayName);
+        generalRequest = false;
+      }
+      generalRequest = generalRequest || !overlay.allowSearchById();
+
+      if (overlay.allowGeneralSearch() || overlay.allowSearchById()) {
+        if (overlay.isMissingDetailData(element, generalRequest)) {
+          ServerConnector.sendOverlayDetailDataRequest(overlayName, element,
+              generalRequest);
+        }
+      }
+    }
+  }
+};
+
+/**
+ * Updates info window identified by element given as a parameter.
+ * 
+ * @param identifiedElement
+ *            element for which info window should be updated
+ */
+CustomMap.prototype.updateInfoWindowForIdentifiedElement = function(
+    identifiedElement) {
+  var infoWindow = this.getInfoWindowForIdentifiedElement(identifiedElement);
+  if (infoWindow == null) {
+    return;
+  } else {
+    infoWindow.update();
+  }
+};
+
+/**
+ * Returns data from all {@link OverlayCollection} for a given alias.
+ * 
+ * @param alias
+ *            {@link Alias} for which overlay data will be returned
+ * @param general
+ *            if true then all elements will be returned, if false then only
+ *            ones availble right now in the overlay
+ * @returns data from all {@link OverlayCollection} for a given alias
+ */
+CustomMap.prototype.getOverlayDataForAlias = function(alias, general) {
+  var identifiedElement = new IdentifiedElement(alias);
+  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
+};
+
+/**
+ * Returns data from all {@link OverlayCollection} for a given reaction.
+ * 
+ * @param reaction
+ *            {@link Reaction} for which overlay data will be returned
+ * @param general
+ *            if true then all elements will be returned, if false then only
+ *            ones availble right now in the overlay
+ * @returns data from all {@link OverlayCollection} for a given alias
+ */
+CustomMap.prototype.getOverlayDataForReaction = function(reaction, general) {
+  var identifiedElement = new IdentifiedElement(reaction);
+  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
+};
+
+/**
+ * Returns data from all {@link OverlayCollection} for a given {@link PointData}
+ * 
+ * @param point
+ *            {@link PointData} for which overlay data will be returned
+ * @returns data from all {@link OverlayCollection} for a given
+ *          {@link PointData}
+ */
+CustomMap.prototype.getOverlayDataForPoint = function(point, general) {
+  var identifiedElement = new IdentifiedElement(point);
+  return this.getOverlayDataForIdentifiedElement(identifiedElement, general);
+};
+
+/**
+ * Returns data from all {@link OverlayCollection} for element identified by the
+ * parameter
+ * 
+ * @param identifiedElement
+ *            {@link IdentifiedElement} for which overlay data will be returned
+ * @returns data from all {@link OverlayCollection} for a given
+ *          {@link IdentifiedElement}
+ */
+CustomMap.prototype.getOverlayDataForIdentifiedElement = function(
+    identifiedElement, general) {
+  if (general === undefined) {
+    logger.warn("general parameter must be defined");
+    general = [];
+  }
+  var result = [];
+  for ( var overlayName in this.overlayCollections) {
+    if (this.overlayCollections.hasOwnProperty(overlayName)) {
+      var overlay = this.overlayCollections[overlayName];
+      if (overlay.allowGeneralSearch() || overlay.allowSearchById()) {
+        var generalFlag = general[overlay.getName()];
+        if (generalFlag === undefined) {
+          logger.warn("General flag for overlay: " + overlay.getName()
+              + " is not defined, assuming false");
+          generalFlag = false;
+        }
+        result.push({
+          overlay : overlay,
+          data : overlay.getDetailDataByIdentifiedElement(identifiedElement,
+              !overlay.allowSearchById() || generalFlag)
+        });
+      }
+    }
+  }
+  return result;
+};
+
+/**
+ * Returns {@link AbstractInfoWindow} for element identified by the parameter.
+ * 
+ * @param identifiedElement
+ *            {@link IdentifiedElement} that determines for which element we
+ *            want {@link AbstractInfoWindow}
+ * @returns {@link AbstractInfoWindow} for element identified by the parameter
+ */
+CustomMap.prototype.getInfoWindowForIdentifiedElement = function(
+    identifiedElement) {
+  var model = this.getSubmodelById(identifiedElement.modelId);
+  var infoWindow = null;
+  if (identifiedElement.type == "ALIAS") {
+    infoWindow = model.getAliasInfoWindowById(identifiedElement.getId());
+  } else if (identifiedElement.type == "POINT") {
+    infoWindow = model.getPointInfoWindowById(identifiedElement.getId());
+  } else if (identifiedElement.type == "REACTION") {
+    infoWindow = model.getReactionInfoWindowById(identifiedElement.getId());
+  } else {
+    logger.error("Unknown type of IdentifiedElement: ", identifiedElement);
+  }
+  return infoWindow;
+};
+
+CustomMap.prototype.getActiveSubmapId = function() {
+  return this._activeSubmapId;
+};
+
+CustomMap.prototype.setActiveSubmapId = function(submapId) {
+  this._activeSubmapId = submapId;
+};
+
+CustomMap.prototype.updateAliasesForLayout = function(layoutId, jsonAliases) {
+  logger.debug("Updating aliases for layout: " + layoutId);
+
+  // init layout data
+  if (this.mapModel.getLayoutDataById(layoutId) == null) {
+    this.mapModel.initLayoutData(layoutId);
+    for (var i = 0; i < this.submaps.length; i++) {
+      this.submaps[i].mapModel.initLayoutData(layoutId);
+    }
+  }
+
+  var aliases = JSON.parse(jsonAliases);
+  for (var i = 0; i < aliases.length; i++) {
+    var alias = aliases[i];
+    var model = this.getSubmodelById(alias.modelId);
+    if (model != 'undefined' && model != null) {
+      model.mapModel.updateAliasForLayout(layoutId, alias);
+      model.getAliasInfoWindowById(alias.idObject).update();
+    } else {
+      logger.warn("Unknown model: " + alias.modelId);
+    }
+  }
+
+  this.retrieveMissingAliases();
+};
+
+CustomMap.prototype.getReferenceGenome = function(type, version) {
+  var result = null;
+  if (this._referenceGenome[type] == null) {
+    this._referenceGenome[type] = [];
+  }
+  if (this._referenceGenome[type][version] == null) {
+    ServerConnector.sendReferenceGenomeDetailRequest(type, version);
+    this._referenceGenome[type][version] = new ReferenceGenome(null);
+    return null;
+  } else {
+    return this._referenceGenome[type][version];
+  }
+};
+
+CustomMap.prototype.updateReferenceGenome = function(type, version, jsonObj) {
+  if (this._referenceGenome[type] == null) {
+    this._referenceGenome[type] = [];
+  }
+  this._referenceGenome[type][version] = new ReferenceGenome(jsonObj);
+  this.refreshInfoWindows();
+}
diff --git a/web/src/main/webapp/resources/js/GuiConnector.js b/web/src/main/webapp/resources/js/GuiConnector.js
index e5bb74b28a..85503c34d5 100644
--- a/web/src/main/webapp/resources/js/GuiConnector.js
+++ b/web/src/main/webapp/resources/js/GuiConnector.js
@@ -1,302 +1,302 @@
-/**
- * This static global object contains set of functions that returns/set data in
- * the Gui (html).
- */
-GuiConnector = new Object();
-
-/**
- * Flag informing if the context menu is visible or not.
- */
-GuiConnector.contextMenuVisible = false;
-
-/**
- * Flag informing if selection menu is visible or not. Selection menu is
- * available when selcting polygon on the map and right clicking on it.
- */
-GuiConnector.selectionMenuVisible = false;
-
-/**
- * X coordinate of the mouse in a browser.
- */
-GuiConnector.xPos = 0;
-
-/**
- * Y coordinate of the mouse in a browser.
- */
-GuiConnector.yPos = 0;
-
-/**
- * List of GET params passed via url.
- */
-GuiConnector.getParams = [];
-
-// find GuiConnector.getParams
-document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function() {
-  function decode(s) {
-    return decodeURIComponent(s.split("+").join(" "));
-  }
-  GuiConnector.getParams[decode(arguments[1])] = decode(arguments[2]);
-});
-
-/**
- * Initialize navigation for left panel tab.
- */
-$(document)
-    .ready(
-        function() {
-          GuiConnector.leftPanelTabNavi = new TabNavi("tabView", {
-            top : "17px"
-          });
-          GuiConnector.searchTabNavi = new TabNavi("tabView:mainForm:dTable", {
-            hideRemaining : false,
-            tabSize : 1,
-            top : "5px"
-          });
-          GuiConnector.drugTabNavi = new TabNavi(
-              "tabView:drugForm:drugResults", {
-                hideRemaining : false,
-                tabSize : 1,
-                top : "5px"
-              });
-          if (document.getElementById("tabView:chemicalForm:chemicalResults") != null) {
-            GuiConnector.chemicalTabNavi = new TabNavi(
-                "tabView:chemicalForm:chemicalResults", {
-                  hideRemaining : false,
-                  tabSize : 1,
-                  top : "5px"
-                });
-          }
-          GuiConnector.miRnaTabNavi = new TabNavi(
-              "tabView:miRNAForm:miRNAResults", {
-                hideRemaining : false,
-                tabSize : 1,
-                top : "5px"
-              });
-        });
-
-/**
- * Returns name of the file with LCSB logo.
- * 
- * @param bigLogo
- *            {@link Boolean} value determining if we want to have big logo or
- *            small one
- */
-GuiConnector.getLcsbLogoImg = function(bigLogo) {
-  if (bigLogo) {
-    return 'lcsb_logo_mid.png';
-  } else {
-    return 'lcsb_logo.png';
-  }
-};
-
-/**
- * Returns name of the file with image that should be presented when we are
- * wainting for data to be loaded.
- */
-GuiConnector.getLoadingImg = function() {
-  return "icons/ajax-loader.gif";
-};
-
-/**
- * Returns home directory for images in the application.
- */
-GuiConnector.getImgPrefix = function() {
-  return "resources/images/";
-};
-
-/**
- * Returns main google maps div tag placed on the webpage.
- */
-GuiConnector.getGoogleMapElement = function() {
-  return document.getElementById(ServerConnector.formIdentifier
-      + ":gmapElement");
-};
-
-/**
- * Shows main google map (by default map is hidden, because it doesn't point to
- * our data from the beginning).
- */
-GuiConnector.showGoogleMap = function() {
-  GuiConnector.getGoogleMapElement().style.visibility = "visible";
-};
-
-/**
- * Shows legend.
- */
-GuiConnector.showLegend = function() {
-  document.getElementById(ServerConnector.formIdentifier + ':legend').style.display = "block";
-};
-
-/**
- * Hides legend.
- */
-GuiConnector.hideLegend = function() {
-  document.getElementById(ServerConnector.formIdentifier + ':legend').style.display = "none";
-};
-
-/**
- * Hides right click menu.
- */
-GuiConnector.hideRightClickMenu = function() {
-  $(PrimeFaces.escapeClientId(ServerConnector.formIdentifier + ':contextMenu'))
-      .hide();
-  this.contextMenuVisible = false;
-};
-
-/**
- * Returns <code>true</code> if right click menu is visible,
- * <code>false</code> otherwise.
- */
-GuiConnector.isRightMenuVisible = function() {
-  return this.contextMenuVisible;
-};
-
-/**
- * Shows right click menu.
- */
-GuiConnector.showRightClickMenu = function(x, y) {
-  $(PrimeFaces.escapeClientId(ServerConnector.formIdentifier + ':contextMenu'))
-      .css({
-        top : y + 'px',
-        left : x + 'px'
-      }).show();
-  this.contextMenuVisible = true;
-
-  if (this.isSelectionMenuVisible) {
-    this.hideSelectionMenu();
-  }
-};
-
-/**
- * Hides selection menu.
- * 
- * @see selectionMenuVisible
- */
-GuiConnector.hideSelectionMenu = function() {
-  $(
-      PrimeFaces.escapeClientId(ServerConnector.formIdentifier
-          + ':selectionContextMenu')).hide();
-  this.selectionMenuVisible = false;
-};
-
-/**
- * Returns <code>true</code> when selection menu is visible,
- * <code>false</code> otherwise.
- * 
- * @see selectionMenuVisible
- */
-GuiConnector.isSelectionMenuVisible = function() {
-  return this.selectionMenuVisible;
-};
-
-/**
- * Shows selection menu.
- * 
- * @see selectionMenuVisible
- */
-GuiConnector.showSelectionMenu = function(x, y) {
-  $(
-      PrimeFaces.escapeClientId(ServerConnector.formIdentifier
-          + ':selectionContextMenu')).css({
-    top : y + 'px',
-    left : x + 'px'
-  }).show();
-  this.selectionMenuVisible = true;
-
-  if (this.isRightMenuVisible()) {
-    this.hideRightClickMenu();
-  }
-};
-
-/**
- * Gets html div where overview images should be visualized.
- * 
- */
-GuiConnector.getOverviewHtmlTag = function() {
-  return document.getElementById(ServerConnector.formIdentifier
-      + ':overviewDialog');
-};
-
-/**
- * Updates coordinates of the mouse in the browser.
- */
-GuiConnector.updateMouseCoordinates = function(x, y) {
-  this.xPos = x;
-  this.yPos = y;
-};
-
-// forser browser to update mouse coordinates whenever mouse move
-jQuery(document).ready(function() {
-  $(document).mousemove(function(e) {
-    GuiConnector.updateMouseCoordinates(e.pageX, e.pageY);
-  });
-});
-
-/**
- * Return html tag for submap visualization.
- * 
- * @param id
- *            identifier of the submodel
- */
-GuiConnector.getHtmlTagForSubmodelId = function(id) {
-  return document.getElementById('_gmapForm:submodelDialog' + id);
-};
-
-/**
- * Returns js Primefaces object for submap visualization.
- * 
- * @param id
- *            identifier of the submodel
- */
-GuiConnector.getJsPopupForSubmodelId = function(id) {
-  return window['submodelDialog' + id];
-};
-
-/**
- * Opens popup for submap visualization.
- * 
- * @param id
- *            identifier of the submodel
- */
-GuiConnector.openDialog = function(id) {
-  var jsVar = GuiConnector.getJsPopupForSubmodelId(id);
-  if (jsVar != null) {
-    var htmlTag = GuiConnector.getHtmlTagForSubmodelId(id);
-    customMap.openSubmodel(id, htmlTag, jsVar);
-  }
-  return false;
-};
-
-GuiConnector.referenceToHtml = function(reference) {
-  if (reference.summary != null && reference.summary != "") {
-    var result = '<div title="' + reference.summary + '">';
-    result += '<a href="' + reference.link + '" target="_blank">'
-        + reference.name + "</a>";
-    // + reference.name + "(" + reference.type + ")</a>";
-    result += "</div>";
-    return result;
-  } else {
-    var result = '<div><a href="' + reference.link + '" target="_blank">'
-        + reference.name + "</a></div>";
-    // + reference.name + "(" + reference.type + ")</a></div>";
-    return result;
-  }
-};
-
-GuiConnector.openSearchPanel = function() {
-  $('a[href$="#tabView:searchTab"]').click();
-};
-
-/**
- * Opens window that informs user data data is being loaded from server.
- */
-GuiConnector.openLoadingDialog = function() {
-  PF('loadingDlg').show();
-};
-
-/**
- * Closes window that informs user data data is being loaded from server.
- */
-GuiConnector.closeLoadingDialog = function() {
-  PF('loadingDlg').hide();
-};
+/**
+ * This static global object contains set of functions that returns/set data in
+ * the Gui (html).
+ */
+GuiConnector = new Object();
+
+/**
+ * Flag informing if the context menu is visible or not.
+ */
+GuiConnector.contextMenuVisible = false;
+
+/**
+ * Flag informing if selection menu is visible or not. Selection menu is
+ * available when selcting polygon on the map and right clicking on it.
+ */
+GuiConnector.selectionMenuVisible = false;
+
+/**
+ * X coordinate of the mouse in a browser.
+ */
+GuiConnector.xPos = 0;
+
+/**
+ * Y coordinate of the mouse in a browser.
+ */
+GuiConnector.yPos = 0;
+
+/**
+ * List of GET params passed via url.
+ */
+GuiConnector.getParams = [];
+
+// find GuiConnector.getParams
+document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function() {
+  function decode(s) {
+    return decodeURIComponent(s.split("+").join(" "));
+  }
+  GuiConnector.getParams[decode(arguments[1])] = decode(arguments[2]);
+});
+
+/**
+ * Initialize navigation for left panel tab.
+ */
+$(document)
+    .ready(
+        function() {
+          GuiConnector.leftPanelTabNavi = new TabNavi("tabView", {
+            top : "17px"
+          });
+          GuiConnector.searchTabNavi = new TabNavi("tabView:mainForm:dTable", {
+            hideRemaining : false,
+            tabSize : 1,
+            top : "5px"
+          });
+          GuiConnector.drugTabNavi = new TabNavi(
+              "tabView:drugForm:drugResults", {
+                hideRemaining : false,
+                tabSize : 1,
+                top : "5px"
+              });
+          if (document.getElementById("tabView:chemicalForm:chemicalResults") != null) {
+            GuiConnector.chemicalTabNavi = new TabNavi(
+                "tabView:chemicalForm:chemicalResults", {
+                  hideRemaining : false,
+                  tabSize : 1,
+                  top : "5px"
+                });
+          }
+          GuiConnector.miRnaTabNavi = new TabNavi(
+              "tabView:miRNAForm:miRNAResults", {
+                hideRemaining : false,
+                tabSize : 1,
+                top : "5px"
+              });
+        });
+
+/**
+ * Returns name of the file with LCSB logo.
+ * 
+ * @param bigLogo
+ *            {@link Boolean} value determining if we want to have big logo or
+ *            small one
+ */
+GuiConnector.getLcsbLogoImg = function(bigLogo) {
+  if (bigLogo) {
+    return 'lcsb_logo_mid.png';
+  } else {
+    return 'lcsb_logo.png';
+  }
+};
+
+/**
+ * Returns name of the file with image that should be presented when we are
+ * wainting for data to be loaded.
+ */
+GuiConnector.getLoadingImg = function() {
+  return "icons/ajax-loader.gif";
+};
+
+/**
+ * Returns home directory for images in the application.
+ */
+GuiConnector.getImgPrefix = function() {
+  return "resources/images/";
+};
+
+/**
+ * Returns main google maps div tag placed on the webpage.
+ */
+GuiConnector.getGoogleMapElement = function() {
+  return document.getElementById(ServerConnector.formIdentifier
+      + ":gmapElement");
+};
+
+/**
+ * Shows main google map (by default map is hidden, because it doesn't point to
+ * our data from the beginning).
+ */
+GuiConnector.showGoogleMap = function() {
+  GuiConnector.getGoogleMapElement().style.visibility = "visible";
+};
+
+/**
+ * Shows legend.
+ */
+GuiConnector.showLegend = function() {
+  document.getElementById(ServerConnector.formIdentifier + ':legend').style.display = "block";
+};
+
+/**
+ * Hides legend.
+ */
+GuiConnector.hideLegend = function() {
+  document.getElementById(ServerConnector.formIdentifier + ':legend').style.display = "none";
+};
+
+/**
+ * Hides right click menu.
+ */
+GuiConnector.hideRightClickMenu = function() {
+  $(PrimeFaces.escapeClientId(ServerConnector.formIdentifier + ':contextMenu'))
+      .hide();
+  this.contextMenuVisible = false;
+};
+
+/**
+ * Returns <code>true</code> if right click menu is visible,
+ * <code>false</code> otherwise.
+ */
+GuiConnector.isRightMenuVisible = function() {
+  return this.contextMenuVisible;
+};
+
+/**
+ * Shows right click menu.
+ */
+GuiConnector.showRightClickMenu = function(x, y) {
+  $(PrimeFaces.escapeClientId(ServerConnector.formIdentifier + ':contextMenu'))
+      .css({
+        top : y + 'px',
+        left : x + 'px'
+      }).show();
+  this.contextMenuVisible = true;
+
+  if (this.isSelectionMenuVisible) {
+    this.hideSelectionMenu();
+  }
+};
+
+/**
+ * Hides selection menu.
+ * 
+ * @see selectionMenuVisible
+ */
+GuiConnector.hideSelectionMenu = function() {
+  $(
+      PrimeFaces.escapeClientId(ServerConnector.formIdentifier
+          + ':selectionContextMenu')).hide();
+  this.selectionMenuVisible = false;
+};
+
+/**
+ * Returns <code>true</code> when selection menu is visible,
+ * <code>false</code> otherwise.
+ * 
+ * @see selectionMenuVisible
+ */
+GuiConnector.isSelectionMenuVisible = function() {
+  return this.selectionMenuVisible;
+};
+
+/**
+ * Shows selection menu.
+ * 
+ * @see selectionMenuVisible
+ */
+GuiConnector.showSelectionMenu = function(x, y) {
+  $(
+      PrimeFaces.escapeClientId(ServerConnector.formIdentifier
+          + ':selectionContextMenu')).css({
+    top : y + 'px',
+    left : x + 'px'
+  }).show();
+  this.selectionMenuVisible = true;
+
+  if (this.isRightMenuVisible()) {
+    this.hideRightClickMenu();
+  }
+};
+
+/**
+ * Gets html div where overview images should be visualized.
+ * 
+ */
+GuiConnector.getOverviewHtmlTag = function() {
+  return document.getElementById(ServerConnector.formIdentifier
+      + ':overviewDialog');
+};
+
+/**
+ * Updates coordinates of the mouse in the browser.
+ */
+GuiConnector.updateMouseCoordinates = function(x, y) {
+  this.xPos = x;
+  this.yPos = y;
+};
+
+// forser browser to update mouse coordinates whenever mouse move
+jQuery(document).ready(function() {
+  $(document).mousemove(function(e) {
+    GuiConnector.updateMouseCoordinates(e.pageX, e.pageY);
+  });
+});
+
+/**
+ * Return html tag for submap visualization.
+ * 
+ * @param id
+ *            identifier of the submodel
+ */
+GuiConnector.getHtmlTagForSubmodelId = function(id) {
+  return document.getElementById('_gmapForm:submodelDialog' + id);
+};
+
+/**
+ * Returns js Primefaces object for submap visualization.
+ * 
+ * @param id
+ *            identifier of the submodel
+ */
+GuiConnector.getJsPopupForSubmodelId = function(id) {
+  return window['submodelDialog' + id];
+};
+
+/**
+ * Opens popup for submap visualization.
+ * 
+ * @param id
+ *            identifier of the submodel
+ */
+GuiConnector.openDialog = function(id) {
+  var jsVar = GuiConnector.getJsPopupForSubmodelId(id);
+  if (jsVar != null) {
+    var htmlTag = GuiConnector.getHtmlTagForSubmodelId(id);
+    customMap.openSubmodel(id, htmlTag, jsVar);
+  }
+  return false;
+};
+
+GuiConnector.referenceToHtml = function(reference) {
+  if (reference.summary != null && reference.summary != "") {
+    var result = '<div title="' + reference.summary + '">';
+    result += '<a href="' + reference.link + '" target="_blank">'
+        + reference.name + "</a>";
+    // + reference.name + "(" + reference.type + ")</a>";
+    result += "</div>";
+    return result;
+  } else {
+    var result = '<div><a href="' + reference.link + '" target="_blank">'
+        + reference.name + "</a></div>";
+    // + reference.name + "(" + reference.type + ")</a></div>";
+    return result;
+  }
+};
+
+GuiConnector.openSearchPanel = function() {
+  $('a[href$="#tabView:searchTab"]').click();
+};
+
+/**
+ * Opens window that informs user data data is being loaded from server.
+ */
+GuiConnector.openLoadingDialog = function() {
+  PF('loadingDlg').show();
+};
+
+/**
+ * Closes window that informs user data data is being loaded from server.
+ */
+GuiConnector.closeLoadingDialog = function() {
+  PF('loadingDlg').hide();
+};
-- 
GitLab