diff --git a/frontend-js/src/main/css/global.css b/frontend-js/src/main/css/global.css
index df34f9477d50c1ee7c93ff5880d19685cbd0681d..c72de9f83c7cbc4dc258674be5bef70f29f49d7c 100644
--- a/frontend-js/src/main/css/global.css
+++ b/frontend-js/src/main/css/global.css
@@ -42,25 +42,25 @@
     clear: both;
 }
 
-.mapChartNameDiv {
+.minerva-chart-name {
     width: 150px;
     float: left;
     display: inline-block;
 }
 
-.mapChartRowEvenDiv {
+.minerva-chart-row-even, .minerva-chart-row-odd {
+    position: relative;
     white-space: nowrap;
     overflow: hidden;
     margin: 1px;
     width: 360px;
+}
+
+.minerva-chart-row-even {
     background-color: #D3D3D3;
 }
 
-.mapChartRowOddDiv {
-    white-space: nowrap;
-    overflow: hidden;
-    margin: 1px;
-    width: 360px;
+.minerva-chart-row-odd {
     background-color: #C0C0C0;
 }
 
diff --git a/frontend-js/src/main/js/Functions.js b/frontend-js/src/main/js/Functions.js
index b04ce60a4f83bda5b68af027d2add63f15c1dffb..b233ee58d6325cbc9a2ed659ee42cc9409f0a316 100644
--- a/frontend-js/src/main/js/Functions.js
+++ b/frontend-js/src/main/js/Functions.js
@@ -1,636 +1,675 @@
-"use strict";
-
-var Promise = require("bluebird");
-var Point = require('./map/canvas/Point');
-
-/* exported logger */
-
-var logger = require('./logger');
-
-var xss = require('xss');
-
-var Functions = {};
-
-/**
- * Bounds value between opt_min and opt_max (result will be not smaller than
- * opt_min and not bigger than opt_max).
- */
-Functions.bound = function (value, minVal, maxVal) {
-  if (minVal !== null && minVal !== undefined) {
-    value = Math.max(value, minVal);
-  }
-  if (maxVal !== null && maxVal !== undefined) {
-    value = Math.min(value, maxVal);
-  }
-  return value;
-};
-
-Functions.degreesToRadians = function (deg) {
-  return deg * (Math.PI / 180);
-};
-
-Functions.radiansToDegrees = function (rad) {
-  return rad / (Math.PI / 180);
-};
-
-Functions.intToColorString = function (value) {
-  /* jslint bitwise: true */
-  var trimmedValue = (value & 0xFFFFFF);
-  var colorStr = trimmedValue.toString(16);
-  while (colorStr.length < 6) {
-    colorStr = "0" + colorStr;
-  }
-  return '#' + colorStr;
-};
-
-/**
- *
- * @param value
- * @returns {number|undefined}
- */
-Functions.getIntOrUndefined = function (value) {
-  if (Functions.isInt(value)) {
-    return value;
-  } else if (value === undefined || value === null) {
-    return undefined;
-  } else {
-    logger.warn("Invalid argument type: " + value);
-    return undefined;
-  }
-};
-
-
-/**
- *
- * @param {string} color
- * @param {number} opacity
- * @returns {string}
- */
-Functions.colorToRgbaString = function (color, opacity) {
-  if (color[0] !== '#' || color.length !== 7) {
-    throw new Error("Expected color in #RRGGBB format");
-  }
-  var red = parseInt(color.substr(1, 2), 16);
-  var green = parseInt(color.substr(3, 2), 16);
-  var blue = parseInt(color.substr(5, 2), 16);
-  var alpha = opacity !== undefined ? opacity : 1.0;
-  return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
-};
-
-/**
- * Returns stack trace.
- *
- * @returns {string} stack trace
- */
-Functions.stackTrace = function () {
-  var err = new Error();
-  return err.stack;
-};
-
-/**
- * Returns the position of the element on html page.
- *
- * @param element
- *          element for which we want to get the position (top left corner)
- *
- * @return {Point} coordinates of the element
- *
- */
-Functions.getPosition = function (element) {
-  var xPosition = 0;
-  var yPosition = 0;
-
-  while (element) {
-    xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
-    yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
-    element = element.offsetParent;
-  }
-  return new Point(xPosition, yPosition);
-};
-
-/**
- * Checks if the point given as a first argument belongs to a polygon defined as
- * a second parameter. Both: point and polygon should use google.map.point
- * class.
- *
- * @param {Point} point
- *          point which we want to check
- *
- * @param {Point[]} polygon
- *          polygon where we check the point
- */
-
-Functions.pointInsidePolygon = function (point, polygon) {
-  var x = point.x;
-  var y = point.y;
-
-  var inside = false;
-  for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
-    var xi = polygon[i].x, yi = polygon[i].y;
-    var xj = polygon[j].x, yj = polygon[j].y;
-    var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
-    if (intersect) {
-      inside = !inside;
-    }
-  }
-  return inside;
-};
-
-/**
- * In a browser variable we store information about browser user is currently
- * using. Right now only IE is supported.
- */
-Functions.browser = {
-  init: function () {
-
-    this.name = "Unknown";
-    this.version = "Unknown";
-
-    if (typeof navigator !== 'undefined') {
-      // Get the user agent string
-      var ua = navigator.userAgent;
-      this.compatibilityMode = false;
-      var re;
-      if (navigator.appName === 'Microsoft Internet Explorer') {
-        this.name = "IE";
-        re = new RegExp("MSIE ([0-9]+[\.0-9]*)");
-        if (re.exec(ua) !== undefined && re.exec(ua) !== null) {
-          this.version = parseFloat(RegExp.$1);
-        }
-        if (ua.indexOf("MSIE 7.0") > -1) {
-          this.compatibilityMode = true;
-        }
-      } else if (navigator.appName === 'Netscape') {
-        this.name = "Other";
-        ua = navigator.userAgent;
-        re = new RegExp("Trident/.*rv[ :]*([0-9]{1,}[\.0-9]{0,})");
-        if (re.exec(ua) !== undefined && re.exec(ua) !== null) {
-          this.version = parseFloat(RegExp.$1);
-        }
-      }
-    }
-  }
-};
-
-Functions.browser.init();
-
-/**
- * Returns true if parameter is integer, false otherwise.
- *
- * @param n
- *          object to check
- */
-Functions.isInt = function (n) {
-  return Number(n) === n && n % 1 === 0;
-};
-
-/**
- * Returns true if parameter is a DOM element, false otherwise.
- *
- * @param o
- *          object to check
- */
-Functions.isDomElement = function (o) {
-  if (!o) {
-    return false;
-  }
-  return (typeof HTMLElement === "object" ? o instanceof HTMLElement : // DOM2
-    o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string");
-};
-
-/**
- *
- * @returns {Promise<string>}
- */
-Functions.overlayToColor = function (elementOverlay) {
-  var self = this;
-  /* jslint bitwise: true */
-  if (elementOverlay === null || elementOverlay === undefined) {
-    return Promise.reject("elementOverlay cannot be null!");
-  } else if (elementOverlay.color !== undefined && elementOverlay.color !== null) {
-    return Promise.resolve(self.intToColorString(elementOverlay.color.rgb));
-  } else {
-    var ratio = 0;
-    var promiseColors;
-    if (elementOverlay.value !== undefined && elementOverlay.value !== null) {
-      if (elementOverlay.value < 0) {
-        ratio = -elementOverlay.value;
-        promiseColors = [ServerConnector.getMinOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
-      } else {
-        ratio = elementOverlay.value;
-        promiseColors = [ServerConnector.getMaxOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
-      }
-    } else {
-      ratio = 1;
-      promiseColors = [ServerConnector.getSimpleOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
-    }
-
-    return Promise.all(promiseColors).then(function (colors) {
-      var maxColor = colors[0];
-      var neutralColor = colors[1];
-
-      ratio = 1 - ratio;
-      var MAX_RED = 0xFF0000;
-      var MAX_GREEN = 0x00FF00;
-      var MAX_BLUE = 0x0000FF;
-
-      var red = maxColor & MAX_RED;
-      var neutralRed = neutralColor & MAX_RED;
-
-      red = red + (neutralRed - red) * ratio;
-      red = parseInt(red);
-      red = red & MAX_RED;
-
-      var green = maxColor & MAX_GREEN;
-      var neutralGreen = neutralColor & MAX_GREEN;
-      green = green + (neutralGreen - green) * ratio;
-      green = parseInt(green);
-      green = green & MAX_GREEN;
-
-      var blue = maxColor & MAX_BLUE;
-      var neutralBlue = neutralColor & MAX_BLUE;
-      blue = blue + (neutralBlue - blue) * ratio;
-      blue = parseInt(blue);
-      blue = blue & MAX_BLUE;
-
-      var color = red | green | blue;
-      return self.intToColorString(color);
-    });
-  }
-};
-
-Functions.getElementByName = function (element, name) {
-  if (element !== undefined) {
-    if (element.getAttribute("name") === name) {
-      return element;
-    }
-    var children = element.children;
-    for (var i = 0; i < children.length; i++) {
-      var child = children[i];
-      var res = this.getElementByName(child, name);
-      if (res !== undefined) {
-        return res;
-      }
-    }
-  }
-  return undefined;
-};
-
-
-/**
- *
- * @param {HTMLElement}element
- * @param {string} name class name
- * @returns {HTMLElement|undefined}
- */
-Functions.getElementByClassName = function (element, name) {
-  if (name.indexOf(".") !== 0) {
-    name = "." + name;
-  }
-  return $(name, element)[0];
-};
-
-/**
- *
- * @param {Object} params
- * @param {string} params.type type of the {HTMLElement} to be created
- * @param {string} [params.inputType] type of the input to be created
- * @param {string} [params.className] css class of the element
- * @param {string} [params.style] css styling
- * @param {string} [params.value]
- * @param {string|HTMLElement} [params.content]
- *
- * @returns {HTMLElement}
- */
-Functions.createElement = function (params) {
-  var result = document.createElement(params.type);
-  if (params.id !== null && params.id !== undefined) {
-    result.id = params.id;
-  }
-  if (params.name !== null && params.name !== undefined) {
-    result.setAttribute("name", params.name);
-  }
-  if (params.className !== null && params.className !== undefined) {
-    result.className = params.className;
-  }
-  if (params.inputType !== null && params.inputType !== undefined) {
-    result.type = params.inputType;
-  }
-  if (params.content !== null && params.content !== undefined) {
-    if (Functions.isDomElement(params.content)) {
-      result.appendChild(params.content);
-    } else if (params.xss !== false) {
-      var content = xss(params.content);
-      if (content !== params.content) {
-        logger.warn("XSS changed content: " + params.content);
-      }
-      result.innerHTML = content;
-    } else {
-      result.innerHTML = params.content;
-    }
-  }
-  if (params.style !== null && params.style !== undefined) {
-    result.style.cssText = params.style;
-  }
-  if (params.onclick !== null && params.onclick !== undefined) {
-    result.onclick = params.onclick;
-  }
-  if (params.onchange !== null && params.onchange !== undefined) {
-    result.onchange = params.onchange;
-  }
-  if (params.href !== null && params.href !== undefined) {
-    result.href = params.href;
-  }
-  if (params.src !== null && params.src !== undefined) {
-    result.src = params.src;
-  }
-  if (params.value !== null && params.value !== undefined) {
-    result.value = params.value;
-  }
-  if (params.title !== null && params.title !== undefined) {
-    result.title = params.title;
-  }
-  if (params.index !== null && params.index !== undefined) {
-    result.index = params.index;
-  }
-  if (params.data !== null && params.data !== undefined) {
-    $(result).attr("data", params.data);
-  }
-  return result;
-};
-
-function sqr(x) {
-  return x * x;
-}
-
-function dist2(v, w) {
-  return sqr(v.x - w.x) + sqr(v.y - w.y);
-}
-
-function distToSegmentSquared(p, v, w) {
-  var l2 = dist2(v, w);
-
-  if (l2 === 0) {
-    return dist2(p, v);
-  }
-
-  var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
-
-  if (t < 0) {
-    return dist2(p, v);
-  }
-  if (t > 1) {
-    return dist2(p, w);
-  }
-
-  return dist2(p, new Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
-}
-
-Functions.distance = function (p1, el2) {
-  if (el2 instanceof Point) {
-    var p2 = el2;
-    return Math.sqrt((Math.pow(p1.x - p2.x, 2)) + (Math.pow(p1.y - p2.y, 2)));
-  } else {
-    return Math.sqrt(distToSegmentSquared(p1, el2.start, el2.end));
-  }
-};
-
-Functions.removeChildren = function (element) {
-  while (element.firstChild) {
-    element.removeChild(element.firstChild);
-  }
-};
-
-/**
- *
- * @returns {Promise} resolved after javascript is loaded
- */
-Functions.loadScript = function (url) {
-  return new Promise(function (resolve) {
-    var scriptExists = false;
-    var scripts = document.getElementsByTagName('script');
-    for (var i = scripts.length; i--;) {
-      if (scripts[i].src === url)
-        scriptExists = true;
-    }
-    if (!scriptExists) {
-      var head = document.getElementsByTagName('head')[0];
-      var script = document.createElement('script');
-      script.type = 'text/javascript';
-      script.src = url;
-      script.onload = function () {
-        resolve();
-      };
-      head.appendChild(script);
-    } else {
-      resolve();
-    }
-  });
-};
-
-Functions.computeMD5 = function (s) {
-  function L(k, d) {
-    return (k << d) | (k >>> (32 - d))
-  }
-
-  function K(G, k) {
-    var I, d, F, H, x;
-    F = (G & 2147483648);
-    H = (k & 2147483648);
-    I = (G & 1073741824);
-    d = (k & 1073741824);
-    x = (G & 1073741823) + (k & 1073741823);
-    if (I & d) {
-      return (x ^ 2147483648 ^ F ^ H)
-    }
-    if (I | d) {
-      if (x & 1073741824) {
-        return (x ^ 3221225472 ^ F ^ H)
-      } else {
-        return (x ^ 1073741824 ^ F ^ H)
-      }
-    } else {
-      return (x ^ F ^ H)
-    }
-  }
-
-  function r(d, F, k) {
-    return (d & F) | ((~d) & k)
-  }
-
-  function q(d, F, k) {
-    return (d & k) | (F & (~k))
-  }
-
-  function p(d, F, k) {
-    return (d ^ F ^ k)
-  }
-
-  function n(d, F, k) {
-    return (F ^ (d | (~k)))
-  }
-
-  function u(G, F, aa, Z, k, H, I) {
-    G = K(G, K(K(r(F, aa, Z), k), I));
-    return K(L(G, H), F)
-  }
-
-  function f(G, F, aa, Z, k, H, I) {
-    G = K(G, K(K(q(F, aa, Z), k), I));
-    return K(L(G, H), F)
-  }
-
-  function D(G, F, aa, Z, k, H, I) {
-    G = K(G, K(K(p(F, aa, Z), k), I));
-    return K(L(G, H), F)
-  }
-
-  function t(G, F, aa, Z, k, H, I) {
-    G = K(G, K(K(n(F, aa, Z), k), I));
-    return K(L(G, H), F)
-  }
-
-  function e(G) {
-    var Z;
-    var F = G.length;
-    var x = F + 8;
-    var k = (x - (x % 64)) / 64;
-    var I = (k + 1) * 16;
-    var aa = Array(I - 1);
-    var d = 0;
-    var H = 0;
-    while (H < F) {
-      Z = (H - (H % 4)) / 4;
-      d = (H % 4) * 8;
-      aa[Z] = (aa[Z] | (G.charCodeAt(H) << d));
-      H++
-    }
-    Z = (H - (H % 4)) / 4;
-    d = (H % 4) * 8;
-    aa[Z] = aa[Z] | (128 << d);
-    aa[I - 2] = F << 3;
-    aa[I - 1] = F >>> 29;
-    return aa
-  }
-
-  function B(x) {
-    var k = "", F = "", G, d;
-    for (d = 0; d <= 3; d++) {
-      G = (x >>> (d * 8)) & 255;
-      F = "0" + G.toString(16);
-      k = k + F.substr(F.length - 2, 2)
-    }
-    return k
-  }
-
-  function J(k) {
-    k = k.replace(/rn/g, "n");
-    var d = "";
-    for (var F = 0; F < k.length; F++) {
-      var x = k.charCodeAt(F);
-      if (x < 128) {
-        d += String.fromCharCode(x)
-      } else {
-        if ((x > 127) && (x < 2048)) {
-          d += String.fromCharCode((x >> 6) | 192);
-          d += String.fromCharCode((x & 63) | 128)
-        } else {
-          d += String.fromCharCode((x >> 12) | 224);
-          d += String.fromCharCode(((x >> 6) & 63) | 128);
-          d += String.fromCharCode((x & 63) | 128)
-        }
-      }
-    }
-    return d
-  }
-
-  var C = Array();
-  var P, h, E, v, g, Y, X, W, V;
-  var S = 7, Q = 12, N = 17, M = 22;
-  var A = 5, z = 9, y = 14, w = 20;
-  var o = 4, m = 11, l = 16, j = 23;
-  var U = 6, T = 10, R = 15, O = 21;
-  s = J(s);
-  C = e(s);
-  Y = 1732584193;
-  X = 4023233417;
-  W = 2562383102;
-  V = 271733878;
-  for (P = 0; P < C.length; P += 16) {
-    h = Y;
-    E = X;
-    v = W;
-    g = V;
-    Y = u(Y, X, W, V, C[P + 0], S, 3614090360);
-    V = u(V, Y, X, W, C[P + 1], Q, 3905402710);
-    W = u(W, V, Y, X, C[P + 2], N, 606105819);
-    X = u(X, W, V, Y, C[P + 3], M, 3250441966);
-    Y = u(Y, X, W, V, C[P + 4], S, 4118548399);
-    V = u(V, Y, X, W, C[P + 5], Q, 1200080426);
-    W = u(W, V, Y, X, C[P + 6], N, 2821735955);
-    X = u(X, W, V, Y, C[P + 7], M, 4249261313);
-    Y = u(Y, X, W, V, C[P + 8], S, 1770035416);
-    V = u(V, Y, X, W, C[P + 9], Q, 2336552879);
-    W = u(W, V, Y, X, C[P + 10], N, 4294925233);
-    X = u(X, W, V, Y, C[P + 11], M, 2304563134);
-    Y = u(Y, X, W, V, C[P + 12], S, 1804603682);
-    V = u(V, Y, X, W, C[P + 13], Q, 4254626195);
-    W = u(W, V, Y, X, C[P + 14], N, 2792965006);
-    X = u(X, W, V, Y, C[P + 15], M, 1236535329);
-    Y = f(Y, X, W, V, C[P + 1], A, 4129170786);
-    V = f(V, Y, X, W, C[P + 6], z, 3225465664);
-    W = f(W, V, Y, X, C[P + 11], y, 643717713);
-    X = f(X, W, V, Y, C[P + 0], w, 3921069994);
-    Y = f(Y, X, W, V, C[P + 5], A, 3593408605);
-    V = f(V, Y, X, W, C[P + 10], z, 38016083);
-    W = f(W, V, Y, X, C[P + 15], y, 3634488961);
-    X = f(X, W, V, Y, C[P + 4], w, 3889429448);
-    Y = f(Y, X, W, V, C[P + 9], A, 568446438);
-    V = f(V, Y, X, W, C[P + 14], z, 3275163606);
-    W = f(W, V, Y, X, C[P + 3], y, 4107603335);
-    X = f(X, W, V, Y, C[P + 8], w, 1163531501);
-    Y = f(Y, X, W, V, C[P + 13], A, 2850285829);
-    V = f(V, Y, X, W, C[P + 2], z, 4243563512);
-    W = f(W, V, Y, X, C[P + 7], y, 1735328473);
-    X = f(X, W, V, Y, C[P + 12], w, 2368359562);
-    Y = D(Y, X, W, V, C[P + 5], o, 4294588738);
-    V = D(V, Y, X, W, C[P + 8], m, 2272392833);
-    W = D(W, V, Y, X, C[P + 11], l, 1839030562);
-    X = D(X, W, V, Y, C[P + 14], j, 4259657740);
-    Y = D(Y, X, W, V, C[P + 1], o, 2763975236);
-    V = D(V, Y, X, W, C[P + 4], m, 1272893353);
-    W = D(W, V, Y, X, C[P + 7], l, 4139469664);
-    X = D(X, W, V, Y, C[P + 10], j, 3200236656);
-    Y = D(Y, X, W, V, C[P + 13], o, 681279174);
-    V = D(V, Y, X, W, C[P + 0], m, 3936430074);
-    W = D(W, V, Y, X, C[P + 3], l, 3572445317);
-    X = D(X, W, V, Y, C[P + 6], j, 76029189);
-    Y = D(Y, X, W, V, C[P + 9], o, 3654602809);
-    V = D(V, Y, X, W, C[P + 12], m, 3873151461);
-    W = D(W, V, Y, X, C[P + 15], l, 530742520);
-    X = D(X, W, V, Y, C[P + 2], j, 3299628645);
-    Y = t(Y, X, W, V, C[P + 0], U, 4096336452);
-    V = t(V, Y, X, W, C[P + 7], T, 1126891415);
-    W = t(W, V, Y, X, C[P + 14], R, 2878612391);
-    X = t(X, W, V, Y, C[P + 5], O, 4237533241);
-    Y = t(Y, X, W, V, C[P + 12], U, 1700485571);
-    V = t(V, Y, X, W, C[P + 3], T, 2399980690);
-    W = t(W, V, Y, X, C[P + 10], R, 4293915773);
-    X = t(X, W, V, Y, C[P + 1], O, 2240044497);
-    Y = t(Y, X, W, V, C[P + 8], U, 1873313359);
-    V = t(V, Y, X, W, C[P + 15], T, 4264355552);
-    W = t(W, V, Y, X, C[P + 6], R, 2734768916);
-    X = t(X, W, V, Y, C[P + 13], O, 1309151649);
-    Y = t(Y, X, W, V, C[P + 4], U, 4149444226);
-    V = t(V, Y, X, W, C[P + 11], T, 3174756917);
-    W = t(W, V, Y, X, C[P + 2], R, 718787259);
-    X = t(X, W, V, Y, C[P + 9], O, 3951481745);
-    Y = K(Y, h);
-    X = K(X, E);
-    W = K(W, v);
-    V = K(V, g)
-  }
-  var i = B(Y) + B(X) + B(W) + B(V);
-  return i.toLowerCase()
-};
-module.exports = Functions;
+"use strict";
+
+var Promise = require("bluebird");
+var Point = require('./map/canvas/Point');
+
+var logger = require('./logger');
+
+var xss = require('xss');
+
+function Functions() {
+  /**
+   * In a browser variable we store information about browser user is currently
+   * using. Right now only IE is supported.
+   */
+  this.browser = {
+    init: function () {
+
+      this.name = "Unknown";
+      this.version = "Unknown";
+
+      if (typeof navigator !== 'undefined') {
+        // Get the user agent string
+        var ua = navigator.userAgent;
+        this.compatibilityMode = false;
+        var re;
+        if (navigator.appName === 'Microsoft Internet Explorer') {
+          this.name = "IE";
+          re = new RegExp("MSIE ([0-9]+[\.0-9]*)");
+          if (re.exec(ua) !== undefined && re.exec(ua) !== null) {
+            this.version = parseFloat(RegExp.$1);
+          }
+          if (ua.indexOf("MSIE 7.0") > -1) {
+            this.compatibilityMode = true;
+          }
+        } else if (navigator.appName === 'Netscape') {
+          this.name = "Other";
+          ua = navigator.userAgent;
+          re = new RegExp("Trident/.*rv[ :]*([0-9]{1,}[\.0-9]{0,})");
+          if (re.exec(ua) !== undefined && re.exec(ua) !== null) {
+            this.version = parseFloat(RegExp.$1);
+          }
+        }
+      }
+    }
+  };
+
+  this.browser.init();
+}
+
+/**
+ * Bounds value between opt_min and opt_max (result will be not smaller than
+ * opt_min and not bigger than opt_max).
+ */
+Functions.prototype.bound = function (value, minVal, maxVal) {
+  if (minVal !== null && minVal !== undefined) {
+    value = Math.max(value, minVal);
+  }
+  if (maxVal !== null && maxVal !== undefined) {
+    value = Math.min(value, maxVal);
+  }
+  return value;
+};
+
+Functions.prototype.degreesToRadians = function (deg) {
+  return deg * (Math.PI / 180);
+};
+
+Functions.prototype.radiansToDegrees = function (rad) {
+  return rad / (Math.PI / 180);
+};
+
+Functions.prototype.intToColorString = function (value) {
+  /* jslint bitwise: true */
+  var trimmedValue = (value & 0xFFFFFF);
+  var colorStr = trimmedValue.toString(16);
+  while (colorStr.length < 6) {
+    colorStr = "0" + colorStr;
+  }
+  return '#' + colorStr;
+};
+
+/**
+ *
+ * @param value
+ * @returns {number|undefined}
+ */
+Functions.prototype.getIntOrUndefined = function (value) {
+  if (this.isInt(value)) {
+    return value;
+  } else if (value === undefined || value === null) {
+    return undefined;
+  } else {
+    logger.warn("Invalid argument type: " + value);
+    return undefined;
+  }
+};
+
+
+/**
+ *
+ * @param {string} color
+ * @param {number} opacity
+ * @returns {string}
+ */
+Functions.prototype.colorToRgbaString = function (color, opacity) {
+  if (color[0] !== '#' || color.length !== 7) {
+    throw new Error("Expected color in #RRGGBB format, but found: " + color);
+  }
+  var red = parseInt(color.substr(1, 2), 16);
+  var green = parseInt(color.substr(3, 2), 16);
+  var blue = parseInt(color.substr(5, 2), 16);
+  var alpha = opacity !== undefined ? opacity : 1.0;
+  return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
+};
+
+
+/**
+ *
+ * @param {LayoutAlias[]|LayoutReaction[]} overlayData
+ * @returns {Promise<Array>}
+ */
+Functions.prototype.overlaysToColorDataStructure = function (overlayData) {
+  var promises = [];
+  for (var i = 0; i < overlayData.length; i++) {
+    promises.push(this.overlayToColor(overlayData[i]));
+  }
+  return Promise.all(promises).then(function (colors) {
+    var countByColor = [], color;
+    for (var i = 0; i < colors.length; i++) {
+      color = colors[i];
+      if (countByColor[color] !== undefined) {
+        countByColor[color]++;
+      } else {
+        countByColor[color] = 1;
+      }
+    }
+    var result = [];
+    for (color in countByColor) {
+      if (countByColor.hasOwnProperty(color)) {
+        result.push({color: color, amount: countByColor[color]});
+      }
+    }
+    var compare = function (a, b) {
+      return a.color.localeCompare(b.color);
+    };
+    result.sort(compare);
+    return result;
+  });
+
+};
+
+/**
+ * Returns stack trace.
+ *
+ * @returns {string} stack trace
+ */
+Functions.prototype.stackTrace = function () {
+  var err = new Error();
+  return err.stack;
+};
+
+/**
+ * Returns the position of the element on html page.
+ *
+ * @param element
+ *          element for which we want to get the position (top left corner)
+ *
+ * @return {Point} coordinates of the element
+ *
+ */
+Functions.prototype.getPosition = function (element) {
+  var xPosition = 0;
+  var yPosition = 0;
+
+  while (element) {
+    xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
+    yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
+    element = element.offsetParent;
+  }
+  return new Point(xPosition, yPosition);
+};
+
+/**
+ * Checks if the point given as a first argument belongs to a polygon defined as
+ * a second parameter. Both: point and polygon should use google.map.point
+ * class.
+ *
+ * @param {Point} point
+ *          point which we want to check
+ *
+ * @param {Point[]} polygon
+ *          polygon where we check the point
+ */
+
+Functions.prototype.pointInsidePolygon = function (point, polygon) {
+  var x = point.x;
+  var y = point.y;
+
+  var inside = false;
+  for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
+    var xi = polygon[i].x, yi = polygon[i].y;
+    var xj = polygon[j].x, yj = polygon[j].y;
+    var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+    if (intersect) {
+      inside = !inside;
+    }
+  }
+  return inside;
+};
+
+/**
+ * Returns true if parameter is integer, false otherwise.
+ *
+ * @param n
+ *          object to check
+ */
+Functions.prototype.isInt = function (n) {
+  return Number(n) === n && n % 1 === 0;
+};
+
+/**
+ * Returns true if parameter is a DOM element, false otherwise.
+ *
+ * @param o
+ *          object to check
+ */
+Functions.prototype.isDomElement = function (o) {
+  if (!o) {
+    return false;
+  }
+  return (typeof HTMLElement === "object" ? o instanceof HTMLElement : // DOM2
+    o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string");
+};
+
+/**
+ *
+ * @param {LayoutAlias|LayoutReaction} elementOverlay
+ *
+ * @returns {Promise<string>}
+ */
+
+Functions.prototype.overlayToColor = function (elementOverlay) {
+  var self = this;
+  if (elementOverlay === null || elementOverlay === undefined) {
+    return Promise.reject("elementOverlay cannot be null!");
+  } else if (elementOverlay.color !== undefined && elementOverlay.color !== null) {
+    return Promise.resolve(self.intToColorString(elementOverlay.color.rgb));
+  } else {
+    var ratio = 0;
+    var promiseColors;
+    if (elementOverlay.value !== undefined && elementOverlay.value !== null) {
+      if (elementOverlay.value < 0) {
+        ratio = -elementOverlay.value;
+        promiseColors = [ServerConnector.getMinOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
+      } else {
+        ratio = elementOverlay.value;
+        promiseColors = [ServerConnector.getMaxOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
+      }
+    } else {
+      ratio = 1;
+      promiseColors = [ServerConnector.getSimpleOverlayColorInt(), ServerConnector.getNeutralOverlayColorInt()];
+    }
+
+    return Promise.all(promiseColors).then(function (colors) {
+      var maxColor = colors[0];
+      var neutralColor = colors[1];
+
+      ratio = 1 - ratio;
+      var MAX_RED = 0xFF0000;
+      var MAX_GREEN = 0x00FF00;
+      var MAX_BLUE = 0x0000FF;
+
+      var red = maxColor & MAX_RED;
+      var neutralRed = neutralColor & MAX_RED;
+
+      red = red + (neutralRed - red) * ratio;
+      red = parseInt(red);
+      red = red & MAX_RED;
+
+      var green = maxColor & MAX_GREEN;
+      var neutralGreen = neutralColor & MAX_GREEN;
+      green = green + (neutralGreen - green) * ratio;
+      green = parseInt(green);
+      green = green & MAX_GREEN;
+
+      var blue = maxColor & MAX_BLUE;
+      var neutralBlue = neutralColor & MAX_BLUE;
+      blue = blue + (neutralBlue - blue) * ratio;
+      blue = parseInt(blue);
+      blue = blue & MAX_BLUE;
+
+      var color = red | green | blue;
+      return self.intToColorString(color);
+    });
+  }
+};
+
+Functions.prototype.getElementByName = function (element, name) {
+  if (element !== undefined) {
+    if (element.getAttribute("name") === name) {
+      return element;
+    }
+    var children = element.children;
+    for (var i = 0; i < children.length; i++) {
+      var child = children[i];
+      var res = this.getElementByName(child, name);
+      if (res !== undefined) {
+        return res;
+      }
+    }
+  }
+  return undefined;
+};
+
+
+/**
+ *
+ * @param {HTMLElement}element
+ * @param {string} name class name
+ * @returns {HTMLElement|undefined}
+ */
+Functions.prototype.getElementByClassName = function (element, name) {
+  if (name.indexOf(".") !== 0) {
+    name = "." + name;
+  }
+  return $(name, element)[0];
+};
+
+/**
+ *
+ * @param {Object} params
+ * @param {string} params.type type of the {HTMLElement} to be created
+ * @param {string} [params.inputType] type of the input to be created
+ * @param {string} [params.className] css class of the element
+ * @param {string} [params.style] css styling
+ * @param {string} [params.value]
+ * @param {string|HTMLElement} [params.content]
+ *
+ * @returns {HTMLElement}
+ */
+Functions.prototype.createElement = function (params) {
+  var result = document.createElement(params.type);
+  if (params.id !== null && params.id !== undefined) {
+    result.id = params.id;
+  }
+  if (params.name !== null && params.name !== undefined) {
+    result.setAttribute("name", params.name);
+  }
+  if (params.className !== null && params.className !== undefined) {
+    result.className = params.className;
+  }
+  if (params.inputType !== null && params.inputType !== undefined) {
+    result.type = params.inputType;
+  }
+  if (params.content !== null && params.content !== undefined) {
+    if (this.isDomElement(params.content)) {
+      result.appendChild(params.content);
+    } else if (params.xss !== false) {
+      var content = xss(params.content);
+      if (content !== params.content) {
+        logger.warn("XSS changed content: " + params.content);
+      }
+      result.innerHTML = content;
+    } else {
+      result.innerHTML = params.content;
+    }
+  }
+  if (params.style !== null && params.style !== undefined) {
+    result.style.cssText = params.style;
+  }
+  if (params.onclick !== null && params.onclick !== undefined) {
+    result.onclick = params.onclick;
+  }
+  if (params.onchange !== null && params.onchange !== undefined) {
+    result.onchange = params.onchange;
+  }
+  if (params.href !== null && params.href !== undefined) {
+    result.href = params.href;
+  }
+  if (params.src !== null && params.src !== undefined) {
+    result.src = params.src;
+  }
+  if (params.value !== null && params.value !== undefined) {
+    result.value = params.value;
+  }
+  if (params.title !== null && params.title !== undefined) {
+    result.title = params.title;
+  }
+  if (params.index !== null && params.index !== undefined) {
+    result.index = params.index;
+  }
+  if (params.data !== null && params.data !== undefined) {
+    $(result).attr("data", params.data);
+  }
+  return result;
+};
+
+function sqr(x) {
+  return x * x;
+}
+
+function dist2(v, w) {
+  return sqr(v.x - w.x) + sqr(v.y - w.y);
+}
+
+function distToSegmentSquared(p, v, w) {
+  var l2 = dist2(v, w);
+
+  if (l2 === 0) {
+    return dist2(p, v);
+  }
+
+  var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
+
+  if (t < 0) {
+    return dist2(p, v);
+  }
+  if (t > 1) {
+    return dist2(p, w);
+  }
+
+  return dist2(p, new Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
+}
+
+Functions.prototype.distance = function (p1, el2) {
+  if (el2 instanceof Point) {
+    var p2 = el2;
+    return Math.sqrt((Math.pow(p1.x - p2.x, 2)) + (Math.pow(p1.y - p2.y, 2)));
+  } else {
+    return Math.sqrt(distToSegmentSquared(p1, el2.start, el2.end));
+  }
+};
+
+Functions.prototype.removeChildren = function (element) {
+  while (element.firstChild) {
+    element.removeChild(element.firstChild);
+  }
+};
+
+/**
+ *
+ * @returns {Promise} resolved after javascript is loaded
+ */
+Functions.prototype.loadScript = function (url) {
+  return new Promise(function (resolve) {
+    var scriptExists = false;
+    var scripts = document.getElementsByTagName('script');
+    for (var i = scripts.length; i--;) {
+      if (scripts[i].src === url)
+        scriptExists = true;
+    }
+    if (!scriptExists) {
+      var head = document.getElementsByTagName('head')[0];
+      var script = document.createElement('script');
+      script.type = 'text/javascript';
+      script.src = url;
+      script.onload = function () {
+        resolve();
+      };
+      head.appendChild(script);
+    } else {
+      resolve();
+    }
+  });
+};
+
+Functions.prototype.computeMD5 = function (s) {
+  function L(k, d) {
+    return (k << d) | (k >>> (32 - d))
+  }
+
+  function K(G, k) {
+    var I, d, F, H, x;
+    F = (G & 2147483648);
+    H = (k & 2147483648);
+    I = (G & 1073741824);
+    d = (k & 1073741824);
+    x = (G & 1073741823) + (k & 1073741823);
+    if (I & d) {
+      return (x ^ 2147483648 ^ F ^ H)
+    }
+    if (I | d) {
+      if (x & 1073741824) {
+        return (x ^ 3221225472 ^ F ^ H)
+      } else {
+        return (x ^ 1073741824 ^ F ^ H)
+      }
+    } else {
+      return (x ^ F ^ H)
+    }
+  }
+
+  function r(d, F, k) {
+    return (d & F) | ((~d) & k)
+  }
+
+  function q(d, F, k) {
+    return (d & k) | (F & (~k))
+  }
+
+  function p(d, F, k) {
+    return (d ^ F ^ k)
+  }
+
+  function n(d, F, k) {
+    return (F ^ (d | (~k)))
+  }
+
+  function u(G, F, aa, Z, k, H, I) {
+    G = K(G, K(K(r(F, aa, Z), k), I));
+    return K(L(G, H), F)
+  }
+
+  function f(G, F, aa, Z, k, H, I) {
+    G = K(G, K(K(q(F, aa, Z), k), I));
+    return K(L(G, H), F)
+  }
+
+  function D(G, F, aa, Z, k, H, I) {
+    G = K(G, K(K(p(F, aa, Z), k), I));
+    return K(L(G, H), F)
+  }
+
+  function t(G, F, aa, Z, k, H, I) {
+    G = K(G, K(K(n(F, aa, Z), k), I));
+    return K(L(G, H), F)
+  }
+
+  function e(G) {
+    var Z;
+    var F = G.length;
+    var x = F + 8;
+    var k = (x - (x % 64)) / 64;
+    var I = (k + 1) * 16;
+    var aa = Array(I - 1);
+    var d = 0;
+    var H = 0;
+    while (H < F) {
+      Z = (H - (H % 4)) / 4;
+      d = (H % 4) * 8;
+      aa[Z] = (aa[Z] | (G.charCodeAt(H) << d));
+      H++
+    }
+    Z = (H - (H % 4)) / 4;
+    d = (H % 4) * 8;
+    aa[Z] = aa[Z] | (128 << d);
+    aa[I - 2] = F << 3;
+    aa[I - 1] = F >>> 29;
+    return aa
+  }
+
+  function B(x) {
+    var k = "", F = "", G, d;
+    for (d = 0; d <= 3; d++) {
+      G = (x >>> (d * 8)) & 255;
+      F = "0" + G.toString(16);
+      k = k + F.substr(F.length - 2, 2)
+    }
+    return k
+  }
+
+  function J(k) {
+    k = k.replace(/rn/g, "n");
+    var d = "";
+    for (var F = 0; F < k.length; F++) {
+      var x = k.charCodeAt(F);
+      if (x < 128) {
+        d += String.fromCharCode(x)
+      } else {
+        if ((x > 127) && (x < 2048)) {
+          d += String.fromCharCode((x >> 6) | 192);
+          d += String.fromCharCode((x & 63) | 128)
+        } else {
+          d += String.fromCharCode((x >> 12) | 224);
+          d += String.fromCharCode(((x >> 6) & 63) | 128);
+          d += String.fromCharCode((x & 63) | 128)
+        }
+      }
+    }
+    return d
+  }
+
+  var C = Array();
+  var P, h, E, v, g, Y, X, W, V;
+  var S = 7, Q = 12, N = 17, M = 22;
+  var A = 5, z = 9, y = 14, w = 20;
+  var o = 4, m = 11, l = 16, j = 23;
+  var U = 6, T = 10, R = 15, O = 21;
+  s = J(s);
+  C = e(s);
+  Y = 1732584193;
+  X = 4023233417;
+  W = 2562383102;
+  V = 271733878;
+  for (P = 0; P < C.length; P += 16) {
+    h = Y;
+    E = X;
+    v = W;
+    g = V;
+    Y = u(Y, X, W, V, C[P + 0], S, 3614090360);
+    V = u(V, Y, X, W, C[P + 1], Q, 3905402710);
+    W = u(W, V, Y, X, C[P + 2], N, 606105819);
+    X = u(X, W, V, Y, C[P + 3], M, 3250441966);
+    Y = u(Y, X, W, V, C[P + 4], S, 4118548399);
+    V = u(V, Y, X, W, C[P + 5], Q, 1200080426);
+    W = u(W, V, Y, X, C[P + 6], N, 2821735955);
+    X = u(X, W, V, Y, C[P + 7], M, 4249261313);
+    Y = u(Y, X, W, V, C[P + 8], S, 1770035416);
+    V = u(V, Y, X, W, C[P + 9], Q, 2336552879);
+    W = u(W, V, Y, X, C[P + 10], N, 4294925233);
+    X = u(X, W, V, Y, C[P + 11], M, 2304563134);
+    Y = u(Y, X, W, V, C[P + 12], S, 1804603682);
+    V = u(V, Y, X, W, C[P + 13], Q, 4254626195);
+    W = u(W, V, Y, X, C[P + 14], N, 2792965006);
+    X = u(X, W, V, Y, C[P + 15], M, 1236535329);
+    Y = f(Y, X, W, V, C[P + 1], A, 4129170786);
+    V = f(V, Y, X, W, C[P + 6], z, 3225465664);
+    W = f(W, V, Y, X, C[P + 11], y, 643717713);
+    X = f(X, W, V, Y, C[P + 0], w, 3921069994);
+    Y = f(Y, X, W, V, C[P + 5], A, 3593408605);
+    V = f(V, Y, X, W, C[P + 10], z, 38016083);
+    W = f(W, V, Y, X, C[P + 15], y, 3634488961);
+    X = f(X, W, V, Y, C[P + 4], w, 3889429448);
+    Y = f(Y, X, W, V, C[P + 9], A, 568446438);
+    V = f(V, Y, X, W, C[P + 14], z, 3275163606);
+    W = f(W, V, Y, X, C[P + 3], y, 4107603335);
+    X = f(X, W, V, Y, C[P + 8], w, 1163531501);
+    Y = f(Y, X, W, V, C[P + 13], A, 2850285829);
+    V = f(V, Y, X, W, C[P + 2], z, 4243563512);
+    W = f(W, V, Y, X, C[P + 7], y, 1735328473);
+    X = f(X, W, V, Y, C[P + 12], w, 2368359562);
+    Y = D(Y, X, W, V, C[P + 5], o, 4294588738);
+    V = D(V, Y, X, W, C[P + 8], m, 2272392833);
+    W = D(W, V, Y, X, C[P + 11], l, 1839030562);
+    X = D(X, W, V, Y, C[P + 14], j, 4259657740);
+    Y = D(Y, X, W, V, C[P + 1], o, 2763975236);
+    V = D(V, Y, X, W, C[P + 4], m, 1272893353);
+    W = D(W, V, Y, X, C[P + 7], l, 4139469664);
+    X = D(X, W, V, Y, C[P + 10], j, 3200236656);
+    Y = D(Y, X, W, V, C[P + 13], o, 681279174);
+    V = D(V, Y, X, W, C[P + 0], m, 3936430074);
+    W = D(W, V, Y, X, C[P + 3], l, 3572445317);
+    X = D(X, W, V, Y, C[P + 6], j, 76029189);
+    Y = D(Y, X, W, V, C[P + 9], o, 3654602809);
+    V = D(V, Y, X, W, C[P + 12], m, 3873151461);
+    W = D(W, V, Y, X, C[P + 15], l, 530742520);
+    X = D(X, W, V, Y, C[P + 2], j, 3299628645);
+    Y = t(Y, X, W, V, C[P + 0], U, 4096336452);
+    V = t(V, Y, X, W, C[P + 7], T, 1126891415);
+    W = t(W, V, Y, X, C[P + 14], R, 2878612391);
+    X = t(X, W, V, Y, C[P + 5], O, 4237533241);
+    Y = t(Y, X, W, V, C[P + 12], U, 1700485571);
+    V = t(V, Y, X, W, C[P + 3], T, 2399980690);
+    W = t(W, V, Y, X, C[P + 10], R, 4293915773);
+    X = t(X, W, V, Y, C[P + 1], O, 2240044497);
+    Y = t(Y, X, W, V, C[P + 8], U, 1873313359);
+    V = t(V, Y, X, W, C[P + 15], T, 4264355552);
+    W = t(W, V, Y, X, C[P + 6], R, 2734768916);
+    X = t(X, W, V, Y, C[P + 13], O, 1309151649);
+    Y = t(Y, X, W, V, C[P + 4], U, 4149444226);
+    V = t(V, Y, X, W, C[P + 11], T, 3174756917);
+    W = t(W, V, Y, X, C[P + 2], R, 718787259);
+    X = t(X, W, V, Y, C[P + 9], O, 3951481745);
+    Y = K(Y, h);
+    X = K(X, E);
+    W = K(W, v);
+    V = K(V, g)
+  }
+  var i = B(Y) + B(X) + B(W) + B(V);
+  return i.toLowerCase()
+};
+
+var singleton = new Functions();
+
+module.exports = singleton;
diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js
index 271aeb78f61889ca31ea9524048fbce63bc14615..977c7f543872ec94e5b2f7f68a7198f987bd5668 100644
--- a/frontend-js/src/main/js/ServerConnector.js
+++ b/frontend-js/src/main/js/ServerConnector.js
@@ -159,7 +159,7 @@ function isSessionExpiredError(error) {
 /**
  *
  * @param params
- * @returns {PromiseLike}
+ * @returns {PromiseLike|Promise}
  */
 ServerConnector.sendRequest = function (params) {
   var self = this;
@@ -231,6 +231,12 @@ ServerConnector._sendRequest = function (params) {
   });
 };
 
+/**
+ *
+ * @param {string} url
+ * @param {Object} params
+ * @returns {Promise}
+ */
 ServerConnector.sendPostRequest = function (url, params) {
   return this.sendRequest({
     method: "POST",
@@ -320,8 +326,8 @@ ServerConnector.createGetParams = function (params, prefix) {
 /**
  *
  * @param {Object} paramObj
- * @param {string} paramObj.type
- * @param {string} [paramObj.url]
+ * @param {string} [paramObj.type]
+ * @param {string} paramObj.url
  * @param {Object} [paramObj.params]
  *
  * @returns {string}
@@ -1200,8 +1206,13 @@ ServerConnector.updateUser = function (user) {
   }).then(function (configuration) {
     return self.updateUserPrivileges({user: user, privileges: user.privilegesToExport(configuration)});
   });
-
 };
+
+/**
+ *
+ * @param {User} user
+ * @returns {Promise}
+ */
 ServerConnector.addUser = function (user) {
   var self = this;
   var queryParams = {
@@ -1219,18 +1230,29 @@ ServerConnector.addUser = function (user) {
   }).then(function (configuration) {
     return self.updateUserPrivileges({user: user, privileges: user.privilegesToExport(configuration)});
   });
-
 };
 
+/**
+ *
+ * @param {string} login
+ * @returns {Promise}
+ */
 ServerConnector.removeUser = function (login) {
   var self = this;
   var queryParams = {
     login: login
   };
   return self.sendDeleteRequest(self.getUserUrl(queryParams));
-
 };
 
+/**
+ *
+ * @param {Object} params
+ * @param {User} params.user
+ * @param {Object} params.privileges
+ *
+ * @returns {Promise}
+ */
 ServerConnector.updateUserPrivileges = function (params) {
   var self = this;
   var queryParams = {
@@ -1253,6 +1275,11 @@ ServerConnector.updateUserPrivileges = function (params) {
   });
 };
 
+/**
+ *
+ * @param {Error} error
+ * @returns {Promise}
+ */
 ServerConnector.processNetworkError = function (error) {
   if ((error instanceof NetworkError)) {
     switch (error.statusCode) {
@@ -1268,7 +1295,14 @@ ServerConnector.processNetworkError = function (error) {
   }
 };
 
-
+/**
+ *
+ * @param {Object} params
+ * @param {User} params.user
+ * @param {UserPreferences} params.preferences
+ *
+ * @returns {Promise}
+ */
 ServerConnector.updateUserPreferences = function (params) {
   var self = this;
   var queryParams = {
@@ -1289,6 +1323,12 @@ ServerConnector.updateUserPreferences = function (params) {
   });
 };
 
+/**
+ *
+ * @param {boolean} forceRefresh
+ *
+ * @returns {Promise}
+ */
 ServerConnector.getUsers = function (forceRefresh) {
   var self = this;
 
@@ -1314,6 +1354,10 @@ ServerConnector.getUsers = function (forceRefresh) {
   }
 };
 
+/**
+ *
+ * @returns {Promise<Array>}
+ */
 ServerConnector.getReferenceGenomes = function () {
   var self = this;
 
@@ -1328,7 +1372,15 @@ ServerConnector.getReferenceGenomes = function () {
   });
 };
 
-
+/**
+ *
+ * @param {Object} [params]
+ * @param {string} [params.creator] user login
+ * @param {boolean} [params.publicOverlay]
+ * @param {string} [params.projectId]
+ *
+ * @returns {Promise<Array>}
+ */
 ServerConnector.getOverlays = function (params) {
   var self = this;
   if (params === undefined) {
@@ -1353,6 +1405,13 @@ ServerConnector.getOverlays = function (params) {
   });
 };
 
+/**
+ *
+ * @param {number} overlayId
+ * @param {string} [projectId]
+ *
+ * @returns {Promise}
+ */
 ServerConnector.getOverlayElements = function (overlayId, projectId) {
   var self = this;
   if (overlayId === undefined) {
@@ -1410,6 +1469,11 @@ ServerConnector.getFullOverlayElement = function (params) {
   });
 };
 
+/**
+ *
+ * @param {string} [projectId]
+ * @returns {Promise}
+ */
 ServerConnector.getProjectId = function (projectId) {
   var self = this;
   if (projectId !== undefined && projectId !== null && projectId !== "") {
@@ -2154,7 +2218,8 @@ ServerConnector.getReferenceGenome = function (params) {
  * @param {string|number} params.genomeId
  * @param {string} params.mappingName
  * @param {string} params.mappingUrl
- * @returns {PromiseLike}
+ *
+ * @returns {Promise}
  */
 ServerConnector.addGeneMapping = function (params) {
   var self = this;
@@ -2413,6 +2478,10 @@ ServerConnector.setPluginUserParam = function (params) {
   });
 };
 
+/**
+ *
+ * @returns {Promise}
+ */
 ServerConnector.getSubmapConnections = function () {
   var self = this;
   var queryParams = {};
diff --git a/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js b/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
index daf9d1d8e00f59be15a2b5b36b02bb8854106717..1bbc720da8c241f3792adc69edae5d5705e3f409 100644
--- a/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
+++ b/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
@@ -166,11 +166,8 @@ UsersAdminPanel.prototype._createUsersTableRow = function () {
       var newIsConnected = $('input:checked', field).val() === "LDAP";
       var isConnected = user.isConnectedToLdap();
       if (isConnected !== newIsConnected) {
-        console.log(newIsConnected);
         user.setConnectedToLdap(newIsConnected);
         return self.getServerConnector().updateUser(user);
-      } else {
-        console.log('dont change');
       }
     }).catch(function (error) {
       GuiConnector.alert(error);
diff --git a/frontend-js/src/main/js/map/AbstractCustomMap.js b/frontend-js/src/main/js/map/AbstractCustomMap.js
index 967fff2cb0a0ce4e92bdb9664a8decb529c778e7..03f7119a22234b8e84ddcd4af47f08c9e4ce3627 100644
--- a/frontend-js/src/main/js/map/AbstractCustomMap.js
+++ b/frontend-js/src/main/js/map/AbstractCustomMap.js
@@ -9,6 +9,7 @@ var AliasInfoWindow = require('./window/AliasInfoWindow');
 var AliasSurface = require('./surface/AliasSurface');
 var GuiConnector = require('../GuiConnector');
 var IdentifiedElement = require('./data/IdentifiedElement');
+var LayoutAlias = require('./data/LayoutAlias');
 var ObjectWithListeners = require('../ObjectWithListeners');
 var MapModel = require('./data/MapModel');
 var Point = require('./canvas/Point');
@@ -362,65 +363,127 @@ AbstractCustomMap.prototype._showSelectedDataOverlay = function (overlayId, inde
   // end ratio
   var endX = (index + 1) * (1.0 / length);
 
-  var overlayAliases;
-  var overlayReactions;
-
   return self.getProject().getDataOverlayById(overlayId).then(function (overlay) {
-    overlayAliases = overlay.getAliases();
-    overlayReactions = overlay.getReactions();
+    return Promise.all([self._showDataOverlayAliases(overlay, startX, endX),
+      self._showDataOverlayReactions(overlay, length === 1)
+    ]);
+  });
+};
 
-    var i, identifiedElements = [];
-    for (i = 0; i < overlayAliases.length; i++) {
-      if (overlayAliases[i].getModelId() === self.getId()) {
-        identifiedElements.push(new IdentifiedElement(overlayAliases[i]));
-      }
+/**
+ *
+ * @param {DataOverlay} overlay
+ * @param {number} startX
+ * @param {number} endX
+ *
+ * @returns {Promise|PromiseLike}
+ * @private
+ */
+AbstractCustomMap.prototype._showDataOverlayAliases = function (overlay, startX, endX) {
+  var self = this;
+  var overlayAliases = overlay.getAliases();
+  var overlayAliasesOnMap = [];
+
+  var i, identifiedElements = [];
+  var usedAliasIds = [];
+  var overlayMapIds = [];
+  for (i = 0; i < overlayAliases.length; i++) {
+    if (overlayMapIds[overlayAliases[i].getModelId()] === undefined) {
+      overlayMapIds[overlayAliases[i].getModelId()] = [];
     }
-    for (i = 0; i < overlayReactions.length; i++) {
-      if (overlayReactions[i].getModelId() === self.getId()) {
-        identifiedElements.push(new IdentifiedElement(overlayReactions[i]));
+    overlayMapIds[overlayAliases[i].getModelId()].push(overlayAliases[i]);
+
+    if (overlayAliases[i].getModelId() === self.getId()) {
+      identifiedElements.push(new IdentifiedElement(overlayAliases[i]));
+      overlayAliasesOnMap.push(overlayAliases[i]);
+      usedAliasIds[overlayAliases[i].getId()] = true;
+    }
+  }
+  var elementsPointingToSubmapPromises = [];
+  for (var mapId in overlayMapIds) {
+    elementsPointingToSubmapPromises.push(self.getProject().getElementsPointingToSubmap(parseInt(mapId)));
+  }
+  return Promise.all(elementsPointingToSubmapPromises).then(function (elementsPointingToSubmap) {
+    for (var i = 0; i < elementsPointingToSubmap.length; i++) {
+      var row = elementsPointingToSubmap[i];
+      for (var j = 0; j < row.length; j++) {
+        var identifiedElement = row[j];
+        if (identifiedElement.getModelId() === self.getId() && !usedAliasIds[identifiedElement.getId()]) {
+          usedAliasIds[identifiedElement.getId()] = true;
+          overlayAliasesOnMap.push(new LayoutAlias({
+            idObject: identifiedElement.getId(),
+            modelId: identifiedElement.getModelId(),
+            value: 0
+          }));
+        }
       }
     }
-    return self.getModel().getByIdentifiedElements(identifiedElements, false);
+    return self.getModel().getByIdentifiedElements(identifiedElements, false)
   }).then(function () {
-    return Promise.each(overlayAliases, function (overlayAlias) {
-      if (overlayAlias.getModelId() === self.getId()) {
-        return self.getModel().getAliasById(overlayAlias.getId()).then(function (aliasData) {
-          var surface = new AliasSurface({
-            overlayAlias: overlayAlias,
-            alias: aliasData,
-            map: self,
-            startX: startX,
-            endX: endX,
-            onClick: [function () {
-              return self.getTopMap().getOverlayByName("search").searchByTarget(new IdentifiedElement(aliasData));
-            }, function () {
-              return self.getTopMap().callListeners("onBioEntityClick", new IdentifiedElement(aliasData));
-            }]
-          });
-          self.selectedLayoutOverlays[overlayId].push(surface);
-          return surface.show();
+    return Promise.each(overlayAliasesOnMap, function (overlayAlias) {
+      return self.getModel().getAliasById(overlayAlias.getId()).then(function (aliasData) {
+        var overlayData;
+        if (aliasData.getLinkedSubmodelId() !== undefined && overlayMapIds[aliasData.getLinkedSubmodelId()] !== undefined) {
+          overlayData = overlayMapIds[aliasData.getLinkedSubmodelId()];
+        } else {
+          overlayData = [overlayAlias];
+        }
+        var surface = new AliasSurface({
+          overlayData: overlayData,
+          alias: aliasData,
+          map: self,
+          startX: startX,
+          endX: endX,
+          onClick: [function () {
+            return self.getTopMap().getOverlayByName("search").searchByTarget(new IdentifiedElement(aliasData));
+          }, function () {
+            return self.getTopMap().callListeners("onBioEntityClick", new IdentifiedElement(aliasData));
+          }]
         });
-      }
+        self.selectedLayoutOverlays[overlay.getId()].push(surface);
+        return surface.show();
+      });
     });
-  }).then(function () {
-    return Promise.each(overlayReactions, function (overlayReaction) {
-      if (overlayReaction.getModelId() === self.getId()) {
-        return self.getModel().getReactionById(overlayReaction.getId()).then(function (reactionData) {
-          var surface = new ReactionSurface({
-            layoutReaction: overlayReaction,
-            reaction: reactionData,
-            map: self,
-            onClick: [function () {
-              return self.getTopMap().getOverlayByName("search").searchByTarget(new IdentifiedElement(reactionData));
-            }, function () {
-              return self.getTopMap().callListeners("onBioEntityClick", new IdentifiedElement(reactionData));
-            }],
-            customized: (length === 1)
-          });
-          self.selectedLayoutOverlays[overlayId].push(surface);
-          return surface.show();
+  });
+};
+
+/**
+ *
+ * @param {DataOverlay} overlay
+ * @param {boolean} customized
+ *
+ * @returns {Promise}
+ * @private
+ */
+AbstractCustomMap.prototype._showDataOverlayReactions = function (overlay, customized) {
+  var self = this;
+  var overlayReactions = overlay.getReactions();
+  var overlayReactionsOnMap = [];
+
+  var i, identifiedElements = [];
+  for (i = 0; i < overlayReactions.length; i++) {
+    if (overlayReactions[i].getModelId() === self.getId()) {
+      identifiedElements.push(new IdentifiedElement(overlayReactions[i]));
+      overlayReactionsOnMap.push(overlayReactions[i]);
+    }
+  }
+  return self.getModel().getByIdentifiedElements(identifiedElements, false).then(function () {
+    return Promise.each(overlayReactionsOnMap, function (overlayReaction) {
+      return self.getModel().getReactionById(overlayReaction.getId()).then(function (reactionData) {
+        var surface = new ReactionSurface({
+          layoutReaction: overlayReaction,
+          reaction: reactionData,
+          map: self,
+          onClick: [function () {
+            return self.getTopMap().getOverlayByName("search").searchByTarget(new IdentifiedElement(reactionData));
+          }, function () {
+            return self.getTopMap().callListeners("onBioEntityClick", new IdentifiedElement(reactionData));
+          }],
+          customized: customized
         });
-      }
+        self.selectedLayoutOverlays[overlay.getId()].push(surface);
+        return surface.show();
+      });
     });
   });
 };
@@ -440,7 +503,7 @@ AbstractCustomMap.prototype._hideSelectedLayout = function (overlayId) {
     return Promise.resolve();
   }
 
-  var promises =[];
+  var promises = [];
   for (var i = 0; i < this.selectedLayoutOverlays[overlayId].length; i++) {
     promises.push(this.selectedLayoutOverlays[overlayId][i].hide());
   }
@@ -508,27 +571,6 @@ AbstractCustomMap.prototype._openInfoWindowForAlias = function (alias, marker) {
   }
 };
 
-/**
- * Returns promise of a list of {@link LayoutAlias} information for a given
- * {@link Alias} in all currently visualized overlays.
- *
- * @param {number} aliasId
- *          identifier of the {@link Alias}
- * @returns {PromiseLike<LayoutAlias[]>| Promise<LayoutAlias[]>} promise of an {Array} with list of {@link LayoutAlias} information
- *          for a given {@link Alias} in all currently visualized overlays
- */
-AbstractCustomMap.prototype.getAliasVisibleLayoutsData = function (aliasId) {
-  var self = this;
-  return self.getTopMap().getVisibleDataOverlays().then(function (visibleDataOverlays) {
-    var result = [];
-    for (var i = 0; i < visibleDataOverlays.length; i++) {
-      var overlay = visibleDataOverlays[i];
-      result.push(overlay.getFullAliasById(aliasId));
-    }
-    return Promise.all(result);
-  });
-};
-
 /**
  * Opens {@link ReactionInfoWindow} for given reaction identifier.
  *
diff --git a/frontend-js/src/main/js/map/canvas/Bounds.js b/frontend-js/src/main/js/map/canvas/Bounds.js
index 210e7484774d27156809bd26498fa32e75cb9352..0cbc6b1f2c88c2616b0248973d5c194bae2bee46 100644
--- a/frontend-js/src/main/js/map/canvas/Bounds.js
+++ b/frontend-js/src/main/js/map/canvas/Bounds.js
@@ -73,4 +73,16 @@ Bounds.prototype.contains = function (point) {
 
 };
 
+/**
+ *
+ * @returns {string}
+ */
+Bounds.prototype.toString = function () {
+  if (this._topLeft === undefined) {
+    return "[NaN]";
+  } else {
+    return "[" + this._topLeft.x + "," + this._topLeft.y + "]-[" + this._rightBottom.x + "," + this._rightBottom.y + "]";
+  }
+};
+
 module.exports = Bounds;
diff --git a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
index 60fb32554f4e23ad3d96ca0f92effd87d5ff3821..2e465b34a8d719c99c9ab9127745803fb3f875e3 100644
--- a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
+++ b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
@@ -232,9 +232,9 @@ GoogleMapsApiCanvas.prototype.createMarker = function (options) {
 /**
  *
  * @param {Bounds} options.bounds
- * @param {string} options.id
- * @param {number} options.fillOpacity
  * @param {string} options.fillColor
+ * @param {number} options.fillOpacity
+ * @param {string} options.id
  * @param {string} options.strokeColor
  * @param {number} options.strokeOpacity
  * @param {number} options.strokeWeight
diff --git a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiRectangle.js b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiRectangle.js
index 97ac9369942a054c256d233fc99e2d10c6ee237a..4490ef861d517ddaf4ce19f58270f3e6fa497c62 100644
--- a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiRectangle.js
+++ b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiRectangle.js
@@ -1,21 +1,23 @@
 "use strict";
 
-var Rectangle = require('../Rectangle');
 var Bounds = require('../Bounds');
+var Point = require('../Point');
+var Rectangle = require('../Rectangle');
 
 // noinspection JSUnusedLocalSymbols
 var logger = require('../../../logger');
 
 /**
  *
- * @param {MapCanvas} options.map
+ * @param {GoogleMapsApiCanvas} options.map
  * @param {Bounds} options.bounds
- * @param {MapCanvas} options.fillOpacity
- * @param {number} options.id
- * @param {number} options.strokeWeight
- * @param {string} options.fillColor
+ * @param {string} [options.fillColor]
+ * @param {{color: string, amount: number}[]} [options.fillGradient]
+ * @param {number} options.fillOpacity
+ * @param {string} options.id
  * @param {string} options.strokeColor
  * @param {number} options.strokeOpacity
+ * @param {number} options.strokeWeight
  *
  * @constructor
  * @extends Rectangle
@@ -24,24 +26,67 @@ function GoogleMapsApiRectangle(options) {
   Rectangle.call(this, options);
 
   var self = this;
+  self._rectangles = [];
+
+
+  var i;
+  var bounds;
+  if (options.fillGradient !== undefined) {
+    var totalAmount = 0;
+    for (i = 0; i < options.fillGradient.length; i++) {
+      totalAmount += options.fillGradient[i].amount;
+    }
+    var y = options.bounds.getTopLeft().y;
+    for (i = 0; i < options.fillGradient.length; i++) {
+      var x1 = options.bounds.getTopLeft().x;
+      var x2 = options.bounds.getRightBottom().x;
+      var y1 = y;
+      var ratio = options.fillGradient[i].amount / totalAmount;
+
+      var y2 = y1 + ratio * (options.bounds.getRightBottom().y - options.bounds.getTopLeft().y);
+      y = y2;
+      bounds = new google.maps.LatLngBounds();
+      bounds.extend(self.getMap().fromPointToLatLng(new Point(x1, y1)));
+      bounds.extend(self.getMap().fromPointToLatLng(new Point(x2, y2)));
+      self.addGoogleRectangle(new google.maps.Rectangle({
+        bounds: bounds,
+        fillOpacity: options.fillOpacity,
+        strokeWeight: 0.0,
+        fillColor: options.fillGradient[i].color
+      }));
+    }
+    bounds = new google.maps.LatLngBounds();
+    bounds.extend(self.getMap().fromPointToLatLng(options.bounds.getTopLeft()));
+    bounds.extend(self.getMap().fromPointToLatLng(options.bounds.getRightBottom()));
+    self.addGoogleRectangle(new google.maps.Rectangle({
+      bounds: bounds,
+      fillOpacity: 0.0,
+      id: options.id,
+      strokeWeight: options.strokeWeight,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity
+    }));
+  } else {
+    bounds = new google.maps.LatLngBounds();
+    bounds.extend(self.getMap().fromPointToLatLng(options.bounds.getTopLeft()));
+    bounds.extend(self.getMap().fromPointToLatLng(options.bounds.getRightBottom()));
+    self.addGoogleRectangle(new google.maps.Rectangle({
+      bounds: bounds,
+      fillOpacity: options.fillOpacity,
+      id: options.id,
+      strokeWeight: options.strokeWeight,
+      fillColor: options.fillColor,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity
+    }));
+  }
 
-  var bounds = new google.maps.LatLngBounds();
-  bounds.extend(this.getMap().fromPointToLatLng(options.bounds.getTopLeft()));
-  bounds.extend(this.getMap().fromPointToLatLng(options.bounds.getRightBottom()));
-
-  this.setGoogleRectangle(new google.maps.Rectangle({
-    bounds: bounds,
-    fillOpacity: options.fillOpacity,
-    id: options.id,
-    strokeWeight: options.strokeWeight,
-    fillColor: options.fillColor,
-    strokeColor: options.strokeColor,
-    strokeOpacity: options.strokeOpacity
-  }));
-
-  google.maps.event.addListener(this.getGoogleRectangle(), "click", function(){
-    return self.callListeners("click");
-  });
+  var rectangles = self.getGoogleRectangles();
+  for (i = 0; i < rectangles.length; i++) {
+    google.maps.event.addListener(rectangles[i], "click", function () {
+      return self.callListeners("click");
+    });
+  }
 }
 
 GoogleMapsApiRectangle.prototype = Object.create(Rectangle.prototype);
@@ -51,33 +96,31 @@ GoogleMapsApiRectangle.prototype.constructor = GoogleMapsApiRectangle;
  *
  * @param {google.maps.Rectangle} rectangle
  */
-GoogleMapsApiRectangle.prototype.setGoogleRectangle = function (rectangle) {
-  this._rectangle = rectangle;
+GoogleMapsApiRectangle.prototype.addGoogleRectangle = function (rectangle) {
+  this._rectangles.push(rectangle);
 };
 
 /**
  *
- * @returns {google.maps.Rectangle}
+ * @returns {google.maps.Rectangle[]}
  */
-GoogleMapsApiRectangle.prototype.getGoogleRectangle = function () {
-  return this._rectangle;
+GoogleMapsApiRectangle.prototype.getGoogleRectangles = function () {
+  return this._rectangles;
 };
 
 GoogleMapsApiRectangle.prototype.show = function () {
-  var googleRectangle = this.getGoogleRectangle();
-  if (googleRectangle.getMap() !== undefined && googleRectangle.getMap() !== null) {
-    logger.warn("Rectangle is already shown");
-  }
-  else {
-    googleRectangle.setMap(this.getMap().getGoogleMap());
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    rectangle.setMap(this.getMap().getGoogleMap());
   }
 };
 
 GoogleMapsApiRectangle.prototype.hide = function () {
-  if (!this.isShown()) {
-    logger.warn("Rectangle is already invisible");
-  } else {
-    this.getGoogleRectangle().setMap(null);
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    rectangle.setMap(null);
   }
 };
 
@@ -86,19 +129,40 @@ GoogleMapsApiRectangle.prototype.hide = function () {
  * @returns {boolean}
  */
 GoogleMapsApiRectangle.prototype.isShown = function () {
-  var googleRectangle = this.getGoogleRectangle();
-  return googleRectangle.getMap() !== null && googleRectangle.getMap() !== undefined;
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    if (rectangle.getMap() !== null && rectangle.getMap() !== undefined) {
+      return true;
+    }
+  }
+  return false;
 };
 
 /**
  *
- * @param bounds {Bounds}
+ * @param newBounds {Bounds}
  */
-GoogleMapsApiRectangle.prototype.setBounds = function (bounds) {
-  var latLngBounds = new google.maps.LatLngBounds();
-  latLngBounds.extend(this.getMap().fromPointToLatLng(bounds.getTopLeft()));
-  latLngBounds.extend(this.getMap().fromPointToLatLng(bounds.getRightBottom()));
-  this.getGoogleRectangle().setBounds(latLngBounds);
+GoogleMapsApiRectangle.prototype.setBounds = function (newBounds) {
+  var oldBounds = this.getBounds();
+
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    var rectangleLatLngBounds = rectangle.getBounds();
+    var currentBounds = new Bounds();
+    currentBounds.extend(this.getMap().fromLatLngToPoint(rectangleLatLngBounds.getSouthWest()));
+    currentBounds.extend(this.getMap().fromLatLngToPoint(rectangleLatLngBounds.getNorthEast()));
+
+    var latLngBounds = new google.maps.LatLngBounds();
+
+    var topLeft = this._transformCoordinates(currentBounds.getTopLeft(), oldBounds, newBounds);
+    var rightBottom = this._transformCoordinates(currentBounds.getRightBottom(), oldBounds, newBounds);
+
+    latLngBounds.extend(this.getMap().fromPointToLatLng(topLeft));
+    latLngBounds.extend(this.getMap().fromPointToLatLng(rightBottom));
+    rectangle.setBounds(latLngBounds);
+  }
 };
 
 /**
@@ -106,10 +170,15 @@ GoogleMapsApiRectangle.prototype.setBounds = function (bounds) {
  * @returns {Bounds}
  */
 GoogleMapsApiRectangle.prototype.getBounds = function () {
-  var latLngBounds = this.getGoogleRectangle().getBounds();
   var result = new Bounds();
-  result.extend(this.getMap().fromLatLngToPoint(latLngBounds.getSouthWest()));
-  result.extend(this.getMap().fromLatLngToPoint(latLngBounds.getNorthEast()));
+
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    var latLngBounds = rectangle.getBounds();
+    result.extend(this.getMap().fromLatLngToPoint(latLngBounds.getSouthWest()));
+    result.extend(this.getMap().fromLatLngToPoint(latLngBounds.getNorthEast()));
+  }
   return result;
 };
 
@@ -118,7 +187,11 @@ GoogleMapsApiRectangle.prototype.getBounds = function () {
  * @param {Object} options
  */
 GoogleMapsApiRectangle.prototype.setOptions = function (options) {
-  this.getGoogleRectangle().setOptions(options);
+  var rectangles = this.getGoogleRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    rectangle.setOptions(options);
+  }
 };
 
 
diff --git a/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerRectangle.js b/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerRectangle.js
index eb0b859ee61e9a8156d3eea4eb6e9f2028ae3841..b9037203fc91b3ca670297309d81ca687f3a8839 100644
--- a/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerRectangle.js
+++ b/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerRectangle.js
@@ -27,29 +27,84 @@ function OpenLayersRectangle(options) {
     options.strokeWeight = 1;
   }
   this._options = options;
+  this._rectangles = [];
+
+  if (options.fillGradient !== undefined) {
+    var totalAmount = 0;
+    for (var i = 0; i < options.fillGradient.length; i++) {
+      totalAmount += options.fillGradient[i].amount;
+    }
+    var y = options.bounds.getTopLeft().y;
+    for (i = 0; i < options.fillGradient.length; i++) {
+      var x1 = options.bounds.getTopLeft().x;
+      var x2 = options.bounds.getRightBottom().x;
+      var y1 = y;
+      var ratio = options.fillGradient[i].amount / totalAmount;
+
+      var y2 = y1 + ratio * (options.bounds.getRightBottom().y - options.bounds.getTopLeft().y);
+      y = y2;
+
+      var bounds = new Bounds(new Point(x1, y1), new Point(x2, y2));
+
+      self.addOpenLayersRectangle(self._createFeature({
+        strokeWeight: 0.5,
+        strokeColor: options.fillGradient[i].color,
+        strokeOpacity: options.fillOpacity,
+        bounds: bounds,
+        fillOpacity: options.fillOpacity,
+        fillColor: options.fillGradient[i].color,
+        source: options.source
+      }));
+    }
+    self.addOpenLayersRectangle(self._createFeature({
+      strokeWeight: options.strokeWeight,
+      strokeColor: options.strokeColor,
+      strokeOpacity: options.strokeOpacity,
+      bounds: options.bounds,
+      fillOpacity: 0.0,
+      id: options.id,
+      fillColor: "#000000",
+      source: options.source
+    }));
+  } else {
+    self.addOpenLayersRectangle(self._createFeature(options));
+  }
 
-  var style = self.createStyle(options);
+}
+
+OpenLayersRectangle.prototype = Object.create(Rectangle.prototype);
+OpenLayersRectangle.prototype.constructor = OpenLayersRectangle;
 
-  var polygon = this.createPolygon(options.bounds);
 
+OpenLayersRectangle.prototype._createFeature = function (options) {
+  var self = this;
+  var style = self.createStyle(options);
+  var polygon;
+  polygon = self.createPolygon(options.bounds);
   var feature = new ol.Feature({
     geometry: polygon,
     id: options.id
   });
 
-  this.setOpenLayersRectangle(feature);
   feature.setStyle(new ol.style.Style({}));
   options.source.addFeature(feature);
 
   feature.__openLayerRectangle = this;
+  /**
+   *
+   * @type {style.Style}
+   * @private
+   */
+  feature.__style = style;
+  return feature;
+};
 
-  this._style = style;
-
-}
-
-OpenLayersRectangle.prototype = Object.create(Rectangle.prototype);
-OpenLayersRectangle.prototype.constructor = OpenLayersRectangle;
 
+/**
+ *
+ * @param {Bounds} bounds
+ * @returns {geom.Polygon}
+ */
 OpenLayersRectangle.prototype.createPolygon = function (bounds) {
   var self = this;
 
@@ -68,6 +123,16 @@ OpenLayersRectangle.prototype.createPolygon = function (bounds) {
   return new ol.geom.Polygon([points]);
 };
 
+/**
+ *
+ * @param {string} options.strokeColor
+ * @param {number} options.strokeOpacity
+ * @param {number} options.strokeWeight
+ * @param {string} options.fillColor
+ * @param {number} options.fillOpacity
+ *
+ * @returns {style.Style}
+ */
 OpenLayersRectangle.prototype.createStyle = function (options) {
   return new ol.style.Style({
     stroke: new ol.style.Stroke({
@@ -80,54 +145,110 @@ OpenLayersRectangle.prototype.createStyle = function (options) {
   });
 };
 
-
-OpenLayersRectangle.prototype.setOpenLayersRectangle = function (rectangle) {
-  this._rectangle = rectangle;
+OpenLayersRectangle.prototype.addOpenLayersRectangle = function (rectangle) {
+  this._rectangles.push(rectangle);
 };
 
-OpenLayersRectangle.prototype.getOpenLayersRectangle = function () {
-  return this._rectangle;
+/**
+ *
+ * @returns {Array}
+ */
+OpenLayersRectangle.prototype.getOpenLayersRectangles = function () {
+  return this._rectangles;
 };
 
+/**
+ *
+ */
 OpenLayersRectangle.prototype.show = function () {
-  return this.getOpenLayersRectangle().setStyle(this._style);
+  var rectangles = this.getOpenLayersRectangles();
+
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    rectangle.setStyle(rectangle.__style);
+  }
 };
 
 OpenLayersRectangle.prototype.hide = function () {
-  return this.getOpenLayersRectangle().setStyle(new ol.style.Style({}));
+  var rectangles = this.getOpenLayersRectangles();
+  var hiddenStyle = new ol.style.Style({});
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    rectangle.setStyle(hiddenStyle);
+  }
 };
 OpenLayersRectangle.prototype.isShown = function () {
-  return this.getOpenLayersRectangle().getStyle().getFill() !== null;
+  var rectangles = this.getOpenLayersRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    if (rectangle.getStyle().getFill() !== null) {
+      return true;
+    }
+  }
 };
 
 /**
  *
- * @param bounds {Bounds}
+ * @param newBounds {Bounds}
  */
-OpenLayersRectangle.prototype.setBounds = function (bounds) {
-  this._options.bounds = bounds;
-  this.getOpenLayersRectangle().setGeometry(this.createPolygon(bounds));
+OpenLayersRectangle.prototype.setBounds = function (newBounds) {
+  var self = this;
+
+  self._options.bounds = newBounds;
+  var oldBounds = self.getBounds();
+
+  var rectangles = self.getOpenLayersRectangles();
+
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    var currentBounds = new Bounds();
+    var extent = rectangle.getGeometry().getExtent();
+
+    var projection1 = [extent[0], extent[1]];
+    currentBounds.extend(self.getMap().fromProjectionToPoint(projection1));
+    var projection2 = [extent[2], extent[3]];
+    currentBounds.extend(self.getMap().fromProjectionToPoint(projection2));
+
+    var topLeft = self._transformCoordinates(currentBounds.getTopLeft(), oldBounds, newBounds);
+    var rightBottom = self._transformCoordinates(currentBounds.getRightBottom(), oldBounds, newBounds);
+
+    rectangle.setGeometry(self.createPolygon(new Bounds(topLeft, rightBottom)));
+  }
+
 };
 
 OpenLayersRectangle.prototype.getBounds = function () {
   var self = this;
-  var extent = self.getOpenLayersRectangle().getGeometry().getExtent();
-
-  var projection1 = [extent[0], extent[1]];
-  var p1 = self.getMap().fromProjectionToPoint(projection1);
-  var projection2 = [extent[2], extent[3]];
-  var p2 = self.getMap().fromProjectionToPoint(projection2);
-  return new Bounds(p1, p2);
+  var bounds = new Bounds();
+  var rectangles = this.getOpenLayersRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    var extent = rectangle.getGeometry().getExtent();
+
+    var projection1 = [extent[0], extent[1]];
+    bounds.extend(self.getMap().fromProjectionToPoint(projection1));
+    var projection2 = [extent[2], extent[3]];
+    bounds.extend(self.getMap().fromProjectionToPoint(projection2));
+  }
+  return bounds;
 };
 
 OpenLayersRectangle.prototype.setOptions = function (options) {
   var self = this;
   self._options = Object.assign(self._options, options);
-  var style = self.createStyle(self._options);
-  if (self.isShown()) {
-    self.getOpenLayersRectangle().setStyle(style);
+  var rectangles = this.getOpenLayersRectangles();
+  for (var i = 0; i < rectangles.length; i++) {
+    var rectangle = rectangles[i];
+    var style = rectangle.__style;
+    if (self._options.fillColor !== undefined) {
+      style.getFill().setColor(Functions.colorToRgbaString(self._options.fillColor, self._options.fillOpacity));
+    }
+
+    if (i === rectangles.length - 1) {
+      style.getStroke().setWidth(self._options.strokeWeight);
+      style.getStroke().setColor(Functions.colorToRgbaString(self._options.strokeColor, self._options.strokeOpacity));
+    }
   }
-  self._style = style;
 };
 
 module.exports = OpenLayersRectangle;
diff --git a/frontend-js/src/main/js/map/canvas/Point.js b/frontend-js/src/main/js/map/canvas/Point.js
index ae53c3343776760b0b4fd26e9809bec824d4ff5d..a579d014ffb342eecdf01f587fc056a9d93dc511 100644
--- a/frontend-js/src/main/js/map/canvas/Point.js
+++ b/frontend-js/src/main/js/map/canvas/Point.js
@@ -22,4 +22,13 @@ function Point(x, y) {
   }
 }
 
+/**
+ *
+ * @param {Point} otherPoint
+ * @returns {number}
+ */
+Point.prototype.distanceTo = function (otherPoint) {
+  return Math.sqrt((otherPoint.x - this.x) * (otherPoint.x - this.x) + (otherPoint.y - this.y) * (otherPoint.y - this.y));
+};
+
 module.exports = Point;
diff --git a/frontend-js/src/main/js/map/canvas/Rectangle.js b/frontend-js/src/main/js/map/canvas/Rectangle.js
index b30ec9c9ea811cd02685665975a8009a7a960260..9da655253c42dc1842b7902122b70d50ea0f67da 100644
--- a/frontend-js/src/main/js/map/canvas/Rectangle.js
+++ b/frontend-js/src/main/js/map/canvas/Rectangle.js
@@ -1,6 +1,7 @@
 "use strict";
 
 var MapCanvas = require('./MapCanvas');
+var Point = require('./Point');
 
 // noinspection JSUnusedLocalSymbols
 var logger = require('../../logger');
@@ -74,4 +75,19 @@ Rectangle.prototype.getMap = function () {
   return this._map;
 };
 
+/**
+ *
+ * @param {Point} point
+ * @param {Bounds} oldBounds
+ * @param {Bounds} newBounds
+ *
+ * @returns {Point}
+ * @protected
+ */
+Rectangle.prototype._transformCoordinates = function (point, oldBounds, newBounds) {
+  var x = newBounds.getTopLeft().x + (point.x - oldBounds.getTopLeft().x) / (oldBounds.getRightBottom().x - oldBounds.getTopLeft().x) * (newBounds.getRightBottom().x - newBounds.getTopLeft().x);
+  var y = newBounds.getTopLeft().y + (point.y - oldBounds.getTopLeft().y) / (oldBounds.getRightBottom().y - oldBounds.getTopLeft().y) * (newBounds.getRightBottom().y - newBounds.getTopLeft().y);
+  return new Point(x, y);
+};
+
 module.exports = Rectangle;
diff --git a/frontend-js/src/main/js/map/data/Alias.js b/frontend-js/src/main/js/map/data/Alias.js
index ad5aa080b4bf5b4ecc3dcd2b428b11e66277f62b..1bfd76989315b1d645d6a6438a4fad8556fbe213 100644
--- a/frontend-js/src/main/js/map/data/Alias.js
+++ b/frontend-js/src/main/js/map/data/Alias.js
@@ -26,6 +26,7 @@ function Alias(javaObject) {
     this.setWidth(javaObject.bounds.width);
     this.setHeight(javaObject.bounds.height);
   }
+  this.setLinkedSubmodelId(javaObject.linkedSubmodel);
 
   if (this._modelId === undefined) {
     throw new Error("ModelId is not defined for alias" + javaObject);
diff --git a/frontend-js/src/main/js/map/data/LayoutAlias.js b/frontend-js/src/main/js/map/data/LayoutAlias.js
index 5f39c3089cb33e44cc5ae7c77c6385f5a424f4d9..e82336b0ce6f72c0e25549a5a4bb2367ec430ee8 100644
--- a/frontend-js/src/main/js/map/data/LayoutAlias.js
+++ b/frontend-js/src/main/js/map/data/LayoutAlias.js
@@ -1,111 +1,116 @@
-"use strict";
-
-var GeneVariant = require('./GeneVariant');
-
-/**
- * Class representing alias visualized in a overlay.
- *
- * @param javaObject
- *          object de-serialized ajax query to the server side
- */
-function LayoutAlias(javaObject) {
-  this.setId(javaObject.idObject);
-  this.setValue(javaObject.value);
-  this.setColor(javaObject.color);
-  this.setModelId(javaObject.modelId);
-  this.setDescription(javaObject.description);
-  if (javaObject.type === undefined) {
-    this.setType(LayoutAlias.LIGHT);
-  } else if (javaObject.type === LayoutAlias.GENETIC_VARIANT) {
-    this.setType(LayoutAlias.GENETIC_VARIANT);
-  } else if (javaObject.type === LayoutAlias.GENERIC) {
-    this.setType(LayoutAlias.GENERIC);
-  } else {
-    throw new Error("Unknown type: " + javaObject.type);
-  }
-
-  this.setGeneVariants([]);
-  if (javaObject.geneVariations !== undefined) {
-    for (var i = 0; i < javaObject.geneVariations.length; i++) {
-      this.addGeneVariant(new GeneVariant(javaObject.geneVariations[i]));
-    }
-  }
-}
-
-LayoutAlias.LIGHT = "LIGHT";
-LayoutAlias.GENETIC_VARIANT = "GENETIC_VARIANT";
-LayoutAlias.GENERIC = "GENERIC";
-
-LayoutAlias.prototype.getId = function () {
-  return this.id;
-};
-
-LayoutAlias.prototype.setId = function (id) {
-  this.id = parseInt(id);
-};
-
-LayoutAlias.prototype.getModelId = function () {
-  return this._modelId;
-};
-
-LayoutAlias.prototype.setModelId = function (modelId) {
-  this._modelId = parseInt(modelId);
-};
-
-LayoutAlias.prototype.getValue = function () {
-  return this.value;
-};
-
-LayoutAlias.prototype.getColor = function () {
-  return this.color;
-};
-
-LayoutAlias.prototype.getType = function () {
-  return this._type;
-};
-
-LayoutAlias.prototype.getGeneVariants = function () {
-  return this._geneVariants;
-};
-
-LayoutAlias.prototype.setValue = function (newValue) {
-  this.value = newValue;
-};
-
-LayoutAlias.prototype.setColor = function (newColor) {
-  this.color = newColor;
-};
-
-LayoutAlias.prototype.setType = function (newType) {
-  this._type = newType;
-};
-
-LayoutAlias.prototype.setGeneVariants = function (newGeneVariants) {
-  this._geneVariants = newGeneVariants;
-};
-
-LayoutAlias.prototype.update = function (alias) {
-  if (!(alias instanceof LayoutAlias)) {
-    throw new Error("Unknown parameter type: " + alias);
-  }
-
-  this.setValue(alias.getValue());
-  this.setColor(alias.getColor());
-  this.setGeneVariants(alias.getGeneVariants());
-  this.setType(alias.getType());
-  this.setDescription(alias.getDescription());
-};
-
-LayoutAlias.prototype.addGeneVariant = function (geneVariant) {
-  this._geneVariants.push(geneVariant);
-};
-
-LayoutAlias.prototype.getDescription = function () {
-  return this._description;
-};
-
-LayoutAlias.prototype.setDescription = function (description) {
-  this._description = description;
-};
-
-module.exports = LayoutAlias;
+"use strict";
+
+var GeneVariant = require('./GeneVariant');
+
+/**
+ * Class representing alias visualized in a overlay.
+ *
+ * @param {number} javaObject.idObject
+ * @param {number} [javaObject.value]
+ * @param {string} [javaObject.color]
+ * @param {number} javaObject.modelId
+ * @param {string} [javaObject.description]
+ * @param {string} [javaObject.type=LayoutAlias.LIGHT]
+ * @param {Array} [javaObject.geneVariations]
+ */
+function LayoutAlias(javaObject) {
+  this.setId(javaObject.idObject);
+  this.setValue(javaObject.value);
+  this.setColor(javaObject.color);
+  this.setModelId(javaObject.modelId);
+  this.setDescription(javaObject.description);
+  if (javaObject.type === undefined) {
+    this.setType(LayoutAlias.LIGHT);
+  } else if (javaObject.type === LayoutAlias.GENETIC_VARIANT) {
+    this.setType(LayoutAlias.GENETIC_VARIANT);
+  } else if (javaObject.type === LayoutAlias.GENERIC) {
+    this.setType(LayoutAlias.GENERIC);
+  } else {
+    throw new Error("Unknown type: " + javaObject.type);
+  }
+
+  this.setGeneVariants([]);
+  if (javaObject.geneVariations !== undefined) {
+    for (var i = 0; i < javaObject.geneVariations.length; i++) {
+      this.addGeneVariant(new GeneVariant(javaObject.geneVariations[i]));
+    }
+  }
+}
+
+LayoutAlias.LIGHT = "LIGHT";
+LayoutAlias.GENETIC_VARIANT = "GENETIC_VARIANT";
+LayoutAlias.GENERIC = "GENERIC";
+
+LayoutAlias.prototype.getId = function () {
+  return this.id;
+};
+
+LayoutAlias.prototype.setId = function (id) {
+  this.id = parseInt(id);
+};
+
+LayoutAlias.prototype.getModelId = function () {
+  return this._modelId;
+};
+
+LayoutAlias.prototype.setModelId = function (modelId) {
+  this._modelId = parseInt(modelId);
+};
+
+LayoutAlias.prototype.getValue = function () {
+  return this.value;
+};
+
+LayoutAlias.prototype.getColor = function () {
+  return this.color;
+};
+
+LayoutAlias.prototype.getType = function () {
+  return this._type;
+};
+
+LayoutAlias.prototype.getGeneVariants = function () {
+  return this._geneVariants;
+};
+
+LayoutAlias.prototype.setValue = function (newValue) {
+  this.value = newValue;
+};
+
+LayoutAlias.prototype.setColor = function (newColor) {
+  this.color = newColor;
+};
+
+LayoutAlias.prototype.setType = function (newType) {
+  this._type = newType;
+};
+
+LayoutAlias.prototype.setGeneVariants = function (newGeneVariants) {
+  this._geneVariants = newGeneVariants;
+};
+
+LayoutAlias.prototype.update = function (alias) {
+  if (!(alias instanceof LayoutAlias)) {
+    throw new Error("Unknown parameter type: " + alias);
+  }
+
+  this.setValue(alias.getValue());
+  this.setColor(alias.getColor());
+  this.setGeneVariants(alias.getGeneVariants());
+  this.setType(alias.getType());
+  this.setDescription(alias.getDescription());
+};
+
+LayoutAlias.prototype.addGeneVariant = function (geneVariant) {
+  this._geneVariants.push(geneVariant);
+};
+
+LayoutAlias.prototype.getDescription = function () {
+  return this._description;
+};
+
+LayoutAlias.prototype.setDescription = function (description) {
+  this._description = description;
+};
+
+module.exports = LayoutAlias;
diff --git a/frontend-js/src/main/js/map/data/MapModel.js b/frontend-js/src/main/js/map/data/MapModel.js
index 81a5dae53e5580ab0fa912bd3b7df2be7257ad81..2173114711ad26566674abb7edb539d4d5d8f0c3 100644
--- a/frontend-js/src/main/js/map/data/MapModel.js
+++ b/frontend-js/src/main/js/map/data/MapModel.js
@@ -256,7 +256,7 @@ MapModel.prototype.getMissingElements = function (elements) {
     } else {
       aliasPromise = ServerConnector.getAliases({
         ids: aliasIds,
-        columns: "id,bounds,modelId"
+        columns: "id,bounds,modelId,linkedSubmodel"
       });
 
     }
diff --git a/frontend-js/src/main/js/map/surface/AliasSurface.js b/frontend-js/src/main/js/map/surface/AliasSurface.js
index f7dc2968bc2c47f7f20d80ad3847b9fc11c9ecdc..b80391ef416accf3cd996390e639767efd78f6d5 100644
--- a/frontend-js/src/main/js/map/surface/AliasSurface.js
+++ b/frontend-js/src/main/js/map/surface/AliasSurface.js
@@ -1,260 +1,291 @@
-"use strict";
-
-/* exported logger */
-
-// noinspection JSUnusedLocalSymbols
-var logger = require('../../logger');
-var functions = require('../../Functions');
-
-var AbstractSurfaceElement = require('./AbstractSurfaceElement');
-var ConfigurationType = require('../../ConfigurationType');
-var IdentifiedElement = require('../data/IdentifiedElement');
-var Bounds = require('../canvas/Bounds');
-var Point = require('../canvas/Point');
-
-/**
- * Class representing overlay of the alias on the map relevant for a specific
- * layout.
- *
- * @param {LayoutAlias} [params.overlayAlias] - {@link LayoutAlias} for which overlay is created
- * @param {number} [params.startX] - this is the ratio on OX axis that should be use as a
- *          starting point of the overlay. For instance when there are three
- *          overlays to visualize then
- *          <ul>
- *          <li>the first layout have startX=0.0; endX=0.33333</li>
- *          <li>second layout have startX=0.33333; endX=0.66666</li>
- *          <li>the last layout have startX=0.66666; endX=1.0</li>
- *          </ul>
- * @param {number} [params.endX] this is the ratio on OX axis that should be use as a
- *          starting point of the overlay
-
- * @param {IdentifiedElement} [params.element]
- * @param {Alias} params.alias
- * @param {AbstractCustomMap} params.map
- * @param {function|function[]} [params.onClick]
- * @constructor
- * @extends AbstractSurfaceElement
- */
-function AliasSurface(params) {
-  // call super constructor
-  AbstractSurfaceElement.call(this, params);
-
-  this.setOverlayData(params.overlayAlias);
-  this.setStartX(params.startX);
-  this.setEndX(params.endX);
-
-  this.setColor(params.color);
-  this.setFillOpacity(params.opacity);
-  this.setStrokeWeight(params.strokeWeight);
-  this.setStrokeColor(params.strokeColor);
-  this.setStrokeOpacity(params.strokeOpacity);
-
-  // original data
-  this.setBioEntity(params.alias);
-  this.setIdentifiedElement(new IdentifiedElement(params.alias));
-}
-
-AliasSurface.prototype = Object.create(AbstractSurfaceElement.prototype);
-AliasSurface.prototype.constructor = AliasSurface;
-
-/**
- *
- * @param {string} color
- */
-AliasSurface.prototype.setColor = function (color) {
-  if (color === undefined) {
-    color = "#FF0000";
-  }
-
-  this._color = color;
-  var mapCanvasObjects = this.getMapCanvasObjects();
-  for (var i = 0; i < mapCanvasObjects.length; i++) {
-    mapCanvasObjects[i].setOptions({
-      strokeColor: color
-    });
-  }
-};
-
-/**
- *
- * @param {number} opacity
- */
-AliasSurface.prototype.setFillOpacity = function (opacity) {
-  this._fillOpacity = opacity;
-};
-
-/**
- *
- * @returns {number|undefined}
- */
-AliasSurface.prototype.getFillOpacity = function () {
-  return this._fillOpacity;
-};
-
-/**
- *
- * @param {number} weight
- */
-AliasSurface.prototype.setStrokeWeight = function (weight) {
-  if (weight === undefined) {
-    weight = 1;
-  }
-  this._strokeWeight = weight;
-};
-
-/**
- *
- * @returns {number}
- */
-AliasSurface.prototype.getStrokeWeight = function () {
-  return this._strokeWeight;
-};
-
-/**
- *
- * @param {string} color
- */
-AliasSurface.prototype.setStrokeColor = function (color) {
-  if (color === undefined) {
-    color = "#000000";
-  }
-  this._strokeColor = color;
-};
-
-/**
- *
- * @returns {string}
- */
-AliasSurface.prototype.getStrokeColor = function () {
-  return this._strokeColor;
-};
-
-/**
- *
- * @param {number} opacity
- */
-AliasSurface.prototype.setStrokeOpacity = function (opacity) {
-  if (opacity === undefined) {
-    opacity = 1;
-  }
-  this._strokeOpacity = opacity;
-};
-
-AliasSurface.prototype.getStrokeOpacity = function () {
-  return this._strokeOpacity;
-};
-
-/**
- * Function used to recalculate boundaries of the {@link AliasSurface}.
- * Boundaries define how big part of original alias is taken by this layout
- * visualization.
- *
- * @param {number} startX
- *          value between 0..1 defining where should be the start on OX axis
- * @param {number} endX
- *          value between 0..1 defining where should be the end on OX axis
- */
-AliasSurface.prototype.setBoundsForAlias = function (startX, endX) {
-  var alias = this.getBioEntity();
-  var pointA = new Point(alias.getX() + startX * alias.getWidth(), alias.getY());
-  var pointB = new Point(alias.getX() + endX * alias.getWidth(), alias.getY() + alias.getHeight());
-
-  var bounds = new Bounds(pointA, pointB);
-  this.getMapCanvasObjects()[0].setBounds(bounds);
-};
-
-/**
- *
- * @returns {PromiseLike<any>}
- */
-AliasSurface.prototype.init = function () {
-  var self = this;
-  var overlayData = self.getOverlayData();
-  var alias = self.getBioEntity();
-  var map = self.getCustomMap();
-  var startX = self.getStartX();
-  var endX = self.getEndX();
-
-  var pointA = new Point(alias.getX() + startX * alias.getWidth(), alias.getY());
-  var pointB = new Point(alias.getX() + endX * alias.getWidth(), alias.getY() + alias.getHeight());
-
-  var bounds = new Bounds(pointA, pointB);
-  var fillOpacity;
-  return ServerConnector.getConfigurationParam(ConfigurationType.OVERLAY_OPACITY).then(function (result) {
-    fillOpacity = self.getFillOpacity();
-    if (fillOpacity === undefined) {
-      fillOpacity = result;
-    }
-    if (overlayData !== undefined) {
-      return functions.overlayToColor(overlayData);
-    } else {
-      return "#FF0000";
-    }
-  }).then(function (color) {
-    self.addMapCanvasObject(map.getMapCanvas().createRectangle({
-      fillOpacity: fillOpacity,
-      strokeColor: self.getStrokeColor(),
-      strokeOpacity: self.getStrokeOpacity(),
-      strokeWeight: self.getStrokeWeight(),
-      fillColor: color,
-      bounds: bounds
-    }));
-  });
-};
-
-/**
- *
- * @returns {LayoutAlias}
- */
-AliasSurface.prototype.getOverlayData = function () {
-  return this._overlayData;
-};
-
-/**
- *
- * @param {LayoutAlias} overlayData
- */
-AliasSurface.prototype.setOverlayData = function (overlayData) {
-  this._overlayData = overlayData;
-};
-
-/**
- *
- * @returns {number}
- */
-AliasSurface.prototype.getStartX = function () {
-  return this._startX;
-};
-
-/**
- *
- * @param {number} startX
- */
-AliasSurface.prototype.setStartX = function (startX) {
-  if (startX === undefined) {
-    startX = 0;
-  }
-  this._startX = startX;
-};
-
-/**
- *
- * @returns {number}
- */
-AliasSurface.prototype.getEndX = function () {
-  return this._endX;
-};
-
-/**
- *
- * @param {number} endX
- */
-AliasSurface.prototype.setEndX = function (endX) {
-  if (endX === undefined) {
-    endX = 1;
-  }
-  this._endX = endX;
-};
-
-
-module.exports = AliasSurface;
+"use strict";
+
+/* exported logger */
+
+// noinspection JSUnusedLocalSymbols
+var logger = require('../../logger');
+var functions = require('../../Functions');
+
+var AbstractSurfaceElement = require('./AbstractSurfaceElement');
+var ConfigurationType = require('../../ConfigurationType');
+var IdentifiedElement = require('../data/IdentifiedElement');
+var Bounds = require('../canvas/Bounds');
+var Point = require('../canvas/Point');
+
+/**
+ * Class representing overlay of the alias on the map relevant for a specific
+ * layout.
+ *
+ * @param {LayoutAlias[]} [params.overlayData] - {@link LayoutAlias} for which overlay is created
+ * @param {number} [params.startX] - this is the ratio on OX axis that should be use as a
+ *          starting point of the overlay. For instance when there are three
+ *          overlays to visualize then
+ *          <ul>
+ *          <li>the first layout have startX=0.0; endX=0.33333</li>
+ *          <li>second layout have startX=0.33333; endX=0.66666</li>
+ *          <li>the last layout have startX=0.66666; endX=1.0</li>
+ *          </ul>
+ * @param {number} [params.endX] this is the ratio on OX axis that should be use as a
+ *          starting point of the overlay
+
+ * @param {IdentifiedElement} [params.element]
+ * @param {Alias} params.alias
+ * @param {AbstractCustomMap} params.map
+ * @param {function|function[]} [params.onClick]
+ * @constructor
+ * @extends AbstractSurfaceElement
+ */
+function AliasSurface(params) {
+  // call super constructor
+  AbstractSurfaceElement.call(this, params);
+  if (params.overlayAlias !== undefined) {
+    throw new Error();
+  }
+
+  this.setOverlayData(params.overlayData);
+  this.setStartX(params.startX);
+  this.setEndX(params.endX);
+
+  this.setColor(params.color);
+  this.setFillOpacity(params.opacity);
+  this.setStrokeWeight(params.strokeWeight);
+  this.setStrokeColor(params.strokeColor);
+  this.setStrokeOpacity(params.strokeOpacity);
+
+  // original data
+  this.setBioEntity(params.alias);
+  this.setIdentifiedElement(new IdentifiedElement(params.alias));
+}
+
+AliasSurface.prototype = Object.create(AbstractSurfaceElement.prototype);
+AliasSurface.prototype.constructor = AliasSurface;
+
+/**
+ *
+ * @param {string} color
+ */
+AliasSurface.prototype.setColor = function (color) {
+  if (color === undefined) {
+    color = "#FF0000";
+  }
+
+  this._color = color;
+  var mapCanvasObjects = this.getMapCanvasObjects();
+  for (var i = 0; i < mapCanvasObjects.length; i++) {
+    mapCanvasObjects[i].setOptions({
+      strokeColor: color
+    });
+  }
+};
+
+/**
+ *
+ * @returns {string}
+ */
+AliasSurface.prototype.getColor = function () {
+  return this._color;
+};
+
+/**
+ *
+ * @param {number} opacity
+ */
+AliasSurface.prototype.setFillOpacity = function (opacity) {
+  this._fillOpacity = opacity;
+};
+
+/**
+ *
+ * @returns {number|undefined}
+ */
+AliasSurface.prototype.getFillOpacity = function () {
+  return this._fillOpacity;
+};
+
+/**
+ *
+ * @param {number} weight
+ */
+AliasSurface.prototype.setStrokeWeight = function (weight) {
+  if (weight === undefined) {
+    weight = 1;
+  }
+  this._strokeWeight = weight;
+};
+
+/**
+ *
+ * @returns {number}
+ */
+AliasSurface.prototype.getStrokeWeight = function () {
+  return this._strokeWeight;
+};
+
+/**
+ *
+ * @param {string} color
+ */
+AliasSurface.prototype.setStrokeColor = function (color) {
+  if (color === undefined) {
+    color = "#000000";
+  }
+  this._strokeColor = color;
+};
+
+/**
+ *
+ * @returns {string}
+ */
+AliasSurface.prototype.getStrokeColor = function () {
+  return this._strokeColor;
+};
+
+/**
+ *
+ * @param {number} opacity
+ */
+AliasSurface.prototype.setStrokeOpacity = function (opacity) {
+  if (opacity === undefined) {
+    opacity = 1;
+  }
+  this._strokeOpacity = opacity;
+};
+
+/**
+ *
+ * @returns {number}
+ */
+AliasSurface.prototype.getStrokeOpacity = function () {
+  return this._strokeOpacity;
+};
+
+/**
+ * Function used to recalculate boundaries of the {@link AliasSurface}.
+ * Boundaries define how big part of original alias is taken by this layout
+ * visualization.
+ *
+ * @param {number} startX
+ *          value between 0..1 defining where should be the start on OX axis
+ * @param {number} endX
+ *          value between 0..1 defining where should be the end on OX axis
+ */
+AliasSurface.prototype.setBoundsForAlias = function (startX, endX) {
+  var alias = this.getBioEntity();
+  var pointA = new Point(alias.getX() + startX * alias.getWidth(), alias.getY());
+  var pointB = new Point(alias.getX() + endX * alias.getWidth(), alias.getY() + alias.getHeight());
+
+  var bounds = new Bounds(pointA, pointB);
+  this.getMapCanvasObjects()[0].setBounds(bounds);
+};
+
+AliasSurface.prototype._computeColors = function () {
+  var self = this;
+  var overlayData = self.getOverlayData();
+  if (overlayData === undefined || overlayData.length === 0) {
+    return self.getColor();
+  } else {
+    return functions.overlaysToColorDataStructure(overlayData);
+  }
+
+};
+/**
+ *
+ * @returns {PromiseLike<any>}
+ */
+AliasSurface.prototype.init = function () {
+  var self = this;
+  var alias = self.getBioEntity();
+  var map = self.getCustomMap();
+  var startX = self.getStartX();
+  var endX = self.getEndX();
+
+  var pointA = new Point(alias.getX() + startX * alias.getWidth(), alias.getY());
+  var pointB = new Point(alias.getX() + endX * alias.getWidth(), alias.getY() + alias.getHeight());
+
+  var bounds = new Bounds(pointA, pointB);
+  var fillOpacity;
+  return map.getServerConnector().getConfigurationParam(ConfigurationType.OVERLAY_OPACITY).then(function (result) {
+    fillOpacity = self.getFillOpacity();
+    if (fillOpacity === undefined) {
+      fillOpacity = result;
+    }
+    return self._computeColors();
+  }).then(function (color) {
+    if (typeof color === 'string' || color instanceof String) {
+      self.addMapCanvasObject(map.getMapCanvas().createRectangle({
+        fillOpacity: fillOpacity,
+        strokeColor: self.getStrokeColor(),
+        strokeOpacity: self.getStrokeOpacity(),
+        strokeWeight: self.getStrokeWeight(),
+        fillColor: color,
+        bounds: bounds
+      }));
+    } else {
+      self.addMapCanvasObject(map.getMapCanvas().createRectangle({
+        fillOpacity: fillOpacity,
+        strokeColor: self.getStrokeColor(),
+        strokeOpacity: self.getStrokeOpacity(),
+        strokeWeight: self.getStrokeWeight(),
+        fillGradient: color,
+        bounds: bounds
+      }));
+    }
+  });
+};
+
+/**
+ *
+ * @returns {LayoutAlias[]}
+ */
+AliasSurface.prototype.getOverlayData = function () {
+  return this._overlayData;
+};
+
+/**
+ *
+ * @param {LayoutAlias[]} overlayData
+ */
+AliasSurface.prototype.setOverlayData = function (overlayData) {
+  this._overlayData = overlayData;
+};
+
+/**
+ *
+ * @returns {number}
+ */
+AliasSurface.prototype.getStartX = function () {
+  return this._startX;
+};
+
+/**
+ *
+ * @param {number} startX
+ */
+AliasSurface.prototype.setStartX = function (startX) {
+  if (startX === undefined) {
+    startX = 0;
+  }
+  this._startX = startX;
+};
+
+/**
+ *
+ * @returns {number}
+ */
+AliasSurface.prototype.getEndX = function () {
+  return this._endX;
+};
+
+/**
+ *
+ * @param {number} endX
+ */
+AliasSurface.prototype.setEndX = function (endX) {
+  if (endX === undefined) {
+    endX = 1;
+  }
+  this._endX = endX;
+};
+
+
+module.exports = AliasSurface;
diff --git a/frontend-js/src/main/js/map/window/AbstractInfoWindow.js b/frontend-js/src/main/js/map/window/AbstractInfoWindow.js
index c174f946a93fbf7a57590a1c94d5a0e60b53f37e..63a91ad29070633a007e64f983e726c8df451051 100644
--- a/frontend-js/src/main/js/map/window/AbstractInfoWindow.js
+++ b/frontend-js/src/main/js/map/window/AbstractInfoWindow.js
@@ -1,743 +1,743 @@
-"use strict";
-
-var Promise = require("bluebird");
-
-var logger = require('../../logger');
-var Functions = require('../../Functions');
-
-var Comment = require('../data/Comment');
-var GuiConnector = require('../../GuiConnector');
-var GuiUtils = require('../../gui/leftPanel/GuiUtils');
-var IdentifiedElement = require('../data/IdentifiedElement');
-var ObjectWithListeners = require('../../ObjectWithListeners');
-var TargettingStructure = require('../data/TargettingStructure');
-
-/**
- * Class representing any info window in our map.
- *
- * @param {IdentifiedElement} params.identifiedElement
- * @param {AbstractCustomMap} params.map
- * @param {Marker} params.marker
- *
- * @constructor
- */
-function AbstractInfoWindow(params) {
-  // call super constructor
-  ObjectWithListeners.call(this);
-
-  var self = this;
-
-  self.setIdentifiedElement(params.identifiedElement);
-
-  self.setCustomMap(params.map);
-  self.setMarker(params.marker);
-
-  self.setContent(this.createWaitingContentDiv());
-
-  self._overlayFullView = [];
-
-  self.registerPropertyType("overlayFullView");
-
-  self.registerListenerType("onShow");
-  self.registerListenerType("onUpdate");
-
-  self.setGuiUtils(new GuiUtils());
-
-  var dbOverlaySearchChanged = function () {
-    return self.update();
-  };
-  var searchDbOverlay = params.map.getTopMap().getOverlayByName("search");
-  if (searchDbOverlay !== undefined) {
-    searchDbOverlay.addListener("onSearch", dbOverlaySearchChanged);
-  }
-  var commentDbOverlay = params.map.getTopMap().getOverlayByName("comment");
-  if (commentDbOverlay !== undefined) {
-    commentDbOverlay.addListener("onSearch", dbOverlaySearchChanged);
-  }
-}
-
-AbstractInfoWindow.prototype = Object.create(ObjectWithListeners.prototype);
-AbstractInfoWindow.prototype.constructor = AbstractInfoWindow;
-
-/**
- * Returns <code>true</code> if overlay should visualize all possible values.
- *
- * @param {string} overlayName
- *          name of the overlay
- * @returns {boolean}, <code>true</code> if overlay should visualize all possible values
- */
-AbstractInfoWindow.prototype.isOverlayFullView = function (overlayName) {
-  if (this._overlayFullView[overlayName] === undefined) {
-    this._overlayFullView[overlayName] = false;
-  }
-  return this._overlayFullView[overlayName];
-};
-
-/**
- * Returns associative array with information if specific overlay should present
- * all possible results or only specified by the data searched by user.
- *
- * @returns {Object.<string,boolean>} with information if specific overlay should present all
- *          possible results or only specified by the data searched by user
- */
-AbstractInfoWindow.prototype.getOverlayFullViewArray = function () {
-  return this._overlayFullView;
-};
-
-/**
- *
- * @param {string} overlayName
- * @param {boolean} value
- * @returns {Promise}
- */
-AbstractInfoWindow.prototype.setOverlayFullView = function (overlayName, value) {
-  var oldVal = this._overlayFullView[overlayName];
-  this._overlayFullView[overlayName] = value;
-  if (oldVal !== value) {
-    return this.firePropertyChangeListener("overlayFullView", overlayName + "," + oldVal, value);
-  } else {
-    return Promise.resolve();
-  }
-};
-
-/**
- * This method checks if {@link AbstractInfoWindow} is opened.
- *
- * @returns {Boolean} <code>true</code> if window is opened,
- *          <code>false</code> otherwise
- */
-AbstractInfoWindow.prototype.isOpened = function () {
-  if (this._infoWindow === undefined) {
-    return false;
-  }
-  return this._infoWindow.isOpened();
-};
-
-/**
- * Opens Info Window.
- *
- * @param {Marker} [newMarker]
- *
- * @returns {PromiseLike<any> | Promise<any>}
- */
-AbstractInfoWindow.prototype.open = function (newMarker) {
-  var self = this;
-  var infoWindow = self._infoWindow;
-  if (infoWindow === null || infoWindow === undefined) {
-    logger.warn("Cannot open window.");
-    return Promise.resolve();
-  }
-  if (newMarker !== undefined) {
-    infoWindow.setMarker(newMarker);
-  }
-  infoWindow.open();
-
-  return self.update().then(function () {
-    return self.callListeners("onShow");
-  });
-};
-
-/**
- * Sets new content of the info window.
- *
- * @param {HTMLElement|string} content
- *          new content of the window
- */
-AbstractInfoWindow.prototype.setContent = function (content) {
-  var self = this;
-  self._content = content;
-  if (self._infoWindow !== undefined) {
-    self._infoWindow.setContent(content);
-  }
-};
-
-/**
- * Returns content visualized in the info window.
- *
- * @returns {string|HTMLElement} content visualized in the info window
- */
-AbstractInfoWindow.prototype.getContent = function () {
-  return this._content;
-};
-
-/**
- * Creates div for an overlay data.
- *
- * @param {AbstractDbOverlay} overlay
- *          corresponding overlay
- * @param {BioEntity[]|Comment[]|Drug[]|MiRna[]|Chemical[]} data
- *          data taken from overlay
- * @returns {HTMLElement} div for given overlay data
- */
-AbstractInfoWindow.prototype.createOverlayInfoDiv = function (overlay, data) {
-  var alias = this.alias;
-  if (alias !== undefined) {
-    if (alias.getType() !== undefined) {
-      if (overlay.name === "drug") {
-        if (alias.getType().toUpperCase() === "RNA" || //
-          alias.getType().toUpperCase() === "PROTEIN" || //
-          alias.getType().toUpperCase() === "GENE") {
-          return this._createDrugInfoDiv(overlay, data);
-        } else {
-          return null;
-        }
-      } else if (overlay.name === "chemical") {
-        if (this.alias.getType().toUpperCase() === "RNA" || //
-          alias.getType().toUpperCase() === "PROTEIN" || //
-          alias.getType().toUpperCase() === "GENE") {
-          return this._createChemicalInfoDiv(overlay, data);
-        } else {
-          return null;
-        }
-      } else if (overlay.name === "mirna") {
-        if (alias.getType().toUpperCase() === "RNA" || //
-          alias.getType().toUpperCase() === "PROTEIN" || //
-          alias.getType().toUpperCase() === "GENE") {
-          return this._createMiRnaInfoDiv(overlay, data);
-        } else {
-          return null;
-        }
-
-      } else if (overlay.name === "comment") {
-        return this._createCommentInfoDiv(overlay, data);
-      } else {
-        logger.warn("Unknown overlay data for AliasInfoWindow: " + overlay.name);
-        return this._createDefaultInfoDiv(overlay, data);
-      }
-    } else {
-      logger.debug(alias);
-      throw new Error("Cannot customize info window. Alias type is unknown ");
-    }
-  } else {
-    if (overlay.getName() === "comment") {
-      return this._createCommentInfoDiv(overlay, data);
-    } else {
-      logger.debug("Cannot customize info window. Alias not defined. Overlay: " + overlay.getName());
-      return null;
-    }
-  }
-};
-
-/**
- * Creates and returns div for drug overlay information.
- *
- * @param {AbstractDbOverlay} overlay
- * @param {Drug[]} data
- *          data taken from drug overlay
- * @returns {HTMLElement} div for drug overlay information
- * @private
- */
-AbstractInfoWindow.prototype._createDrugInfoDiv = function (overlay, data) {
-  return this._createTargetInfoDiv({
-    overlay: overlay,
-    data: data,
-    name: "Interacting drugs"
-  });
-};
-
-/**
- * Creates and returns div for comment overlay information.
- *
- * @param {AbstractDbOverlay} overlay
- * @param {Comment[]} data
- *          data taken from comment overlay
- * @returns {HTMLElement} div for comment overlay information
- */
-AbstractInfoWindow.prototype._createCommentInfoDiv = function (overlay, data) {
-  if (data.length === 0 || data[0] === undefined) {
-    return null;
-  }
-  var result = document.createElement("div");
-
-  var titleElement = document.createElement("h3");
-  titleElement.innerHTML = "Comments";
-  result.appendChild(titleElement);
-  for (var i = 0; i < data.length; i++) {
-    var comment = data[i];
-    if (comment instanceof Comment) {
-      if (!comment.isRemoved()) {
-        result.appendChild(document.createElement("hr"));
-        var commentId = document.createElement("div");
-        commentId.innerHTML = '#' + comment.getId();
-        result.appendChild(commentId);
-        result.appendChild(document.createElement("br"));
-        var commentContent = Functions.createElement({type: "div", content: comment.getContent(), xss: true});
-
-        result.appendChild(commentContent);
-      }
-    } else {
-      throw new Error("Invalid comment data: " + comment);
-    }
-  }
-
-  return result;
-};
-
-/**
- * Creates and returns div for unknown overlay.
- *
- * @param {AbstractDbOverlay} overlay
- * @param {Array} data
- *          data taken from overlay
- * @returns {HTMLElement} div for overlay information
- */
-
-AbstractInfoWindow.prototype._createDefaultInfoDiv = function (overlay, data) {
-  var divElement = document.createElement("div");
-  var count = 0;
-
-  var titleElement = document.createElement("h3");
-  var title = document.createTextNode(overlay.getName());
-  titleElement.appendChild(title);
-  divElement.appendChild(titleElement);
-  for (var searchId in data) {
-    if (data.hasOwnProperty(searchId) && data[searchId] !== undefined && data[searchId] !== null) {
-      count++;
-      var resultTitleElement = document.createElement("h4");
-      var resultTitle = document.createTextNode(searchId);
-      resultTitleElement.appendChild(resultTitle);
-      divElement.appendChild(resultTitleElement);
-
-      var keys = Object.keys(data[searchId]);
-      for (var i = 0; i < keys.length; i++) {
-        var resultValElement = document.createElement("p");
-        var resultVal = document.createTextNode(keys[i] + ": " + data[searchId][keys[i]]);
-        resultValElement.appendChild(resultVal);
-        divElement.appendChild(resultValElement);
-      }
-    }
-  }
-
-  if (count === 0) {
-    divElement = null;
-  }
-  return divElement;
-};
-
-/**
- * Returns Marker object where this info window is attached.
- *
- * @returns {Marker} object where this info window is attached
- */
-AbstractInfoWindow.prototype.getMarker = function () {
-  return this._marker;
-};
-
-/**
- *
- * @param {Marker} marker
- */
-AbstractInfoWindow.prototype.setMarker = function (marker) {
-  this._marker = marker;
-  if (this._infoWindow !== undefined) {
-    this._infoWindow.setMarker(marker);
-  }
-};
-
-/**
- * Returns {@link AbstractCustomMap} where this window is presented.
- *
- * @returns {AbstractCustomMap} where this window is presented
- */
-AbstractInfoWindow.prototype.getCustomMap = function () {
-  return this.customMap;
-};
-
-/**
- *
- * @param {AbstractCustomMap} map
- */
-AbstractInfoWindow.prototype.setCustomMap = function (map) {
-  if (map === undefined) {
-    throw new Error("Map must be defined");
-  }
-  this.customMap = map;
-};
-
-/**
- * Returns html DOM object with content that should presented when waiting for
- * some data from server.
- *
- * @returns {HTMLElement} html with content that should presented when waiting for
- *          some data from server
- */
-AbstractInfoWindow.prototype.createWaitingContentDiv = function () {
-  var result = document.createElement("div");
-  var img = document.createElement("img");
-  img.src = GuiConnector.getImgPrefix() + GuiConnector.getLoadingImg();
-  var message = document.createElement("h4");
-  message.innerHTML = "loading...";
-  result.appendChild(img);
-  result.appendChild(message);
-  return result;
-};
-
-/**
- * This is a generic method that updates content of the window.
- *
- * @returns {Promise|PromiseLike}
- * @private
- */
-AbstractInfoWindow.prototype._updateContent = function () {
-  var contentDiv = null;
-  var self = this;
-
-  if (!self.isOpened()) {
-    return Promise.resolve();
-  } else {
-    self.setContent(self.createWaitingContentDiv());
-
-    return self.createContentDiv().then(function (content) {
-      contentDiv = content;
-      return self.createOverlaysDiv();
-    }).then(function (overlaysDiv) {
-      if (overlaysDiv !== undefined && overlaysDiv !== null) {
-        contentDiv.appendChild(overlaysDiv);
-      }
-      self.setContent(contentDiv);
-      return self.callListeners("onUpdate");
-    }).then(function () {
-      return contentDiv;
-    });
-  }
-};
-
-/**
- * Creates and returns div with overlays content.
- *
- * @returns {Promise<HTMLElement>} with html representing data taken from
- *          {@link AbstractDbOverlay} for this window
- */
-AbstractInfoWindow.prototype.createOverlaysDiv = function () {
-  var self = this;
-  var result = document.createElement("div");
-  return this.getOverlaysData(self.getOverlayFullViewArray()).then(function (overlayData) {
-    for (var i = 0; i < overlayData.length; i++) {
-      var overlay = overlayData[i].overlay;
-      var data = overlayData[i].data;
-      var overlayInfo = self.createOverlayInfoDiv(overlay, data);
-      if (overlayInfo !== null) {
-        result.appendChild(overlayInfo);
-      }
-    }
-    return result;
-  });
-};
-
-// noinspection JSUnusedLocalSymbols
-/**
- * Returns array with data taken from all known {@link AbstractDbOverlay}.
- *
- * @param {Object.<string,boolean>} general
- *          if true then all elements will be returned, if false then only ones
- *          available right now in the overlay
- *
- * @returns {Promise} array with data from {@link AbstractDbOverlay}
- */
-AbstractInfoWindow.prototype.getOverlaysData = function (general) {
-  throw new Error("Not implemented");
-};
-
-/**
- * Abstract method (to be implemented by subclasses) for updating content.
- *
- * @returns {Promise}
- */
-AbstractInfoWindow.prototype.update = function () {
-  return this._updateContent();
-};
-
-/**
- *
- * @param {string} params.name
- * @param {AbstractDbOverlay} params.overlay
- * @param {Array} params.data
- * @returns {HTMLElement}
- * @private
- */
-AbstractInfoWindow.prototype._createTargetInfoDiv = function (params) {
-  var overlay = params.overlay;
-  var data = params.data;
-  var name = params.name;
-
-  var self = this;
-  var result = document.createElement("div");
-
-  var titleElement = document.createElement("h3");
-  titleElement.innerHTML = name;
-  result.appendChild(titleElement);
-  if (overlay.allowGeneralSearch()) {
-    var checkboxDiv = document.createElement("div");
-    checkboxDiv.style.float = "right";
-
-    var checkbox = document.createElement("input");
-    checkbox.id = "checkbox-" + name + "-" + this.getElementType() + "-" + this.getElementId();
-    checkbox.type = "checkbox";
-    checkbox.checked = self.isOverlayFullView(overlay.getName());
-    checkbox.onclick = function () {
-      return self.setOverlayFullView(overlay.getName(), this.checked).then(null, GuiConnector.alert);
-    };
-
-    checkboxDiv.appendChild(checkbox);
-
-    var description = document.createElement("div");
-    description.style.float = "right";
-    description.innerHTML = "Show all";
-    checkboxDiv.appendChild(description);
-    result.appendChild(checkboxDiv);
-  }
-  var count = 0;
-  for (var dataId in data) {
-    if (data.hasOwnProperty(dataId)) {
-      count++;
-    }
-  }
-
-  var table = self._createTableForTargetDiv(data, overlay);
-
-  if (count === 0 && !overlay.allowGeneralSearch() && !this.isOverlayFullView(overlay.getName())) {
-    result = null;
-  }
-  if (result !== null) {
-    result.appendChild(table);
-  }
-  return result;
-};
-
-/**
- *
- * @param {Array} data
- * @param {AbstractDbOverlay} overlay
- * @returns {HTMLElement}
- * @private
- */
-AbstractInfoWindow.prototype._createTableForTargetDiv = function (data, overlay) {
-  var self = this;
-  var table = document.createElement("table");
-  table.className = "minerva-window-drug-table";
-  var header = document.createElement("tr");
-  var headerCol = document.createElement("th");
-  headerCol.innerHTML = "Name";
-  header.appendChild(headerCol);
-  headerCol = document.createElement("th");
-  headerCol.innerHTML = "References";
-  header.appendChild(headerCol);
-
-  var cell;
-  table.appendChild(header);
-  var row;
-
-  var onclick = function () {
-    // ';' enforces single query (in case there are ',' characters in the name)
-    return overlay.searchByQuery(this.innerHTML + ";");
-  };
-
-  var count = 0;
-  for (var searchId in data) {
-    if (data.hasOwnProperty(searchId)) {
-
-      row = document.createElement("tr");
-      var nameContent = searchId;
-      var annotations = [];
-      if (typeof data[searchId] === "string") {
-        nameContent = data[searchId];
-      } else if (data[searchId] instanceof TargettingStructure) {
-        nameContent = data[searchId].getName();
-        var targets = data[searchId].getTargetsForIdentifiedElement(self.getIdentifiedElement());
-        for (var i = 0; i < targets.length; i++) {
-          var references = targets[i].getReferences();
-          for (var j = 0; j < references.length; j++) {
-            annotations.push(references[j]);
-          }
-        }
-      }
-      var link = Functions.createElement({
-        type: "a",
-        onclick: onclick,
-        href: "#",
-        content: nameContent
-      });
-
-      var nameTd = Functions.createElement({
-        type: "td"
-      });
-      nameTd.appendChild(link);
-      row.appendChild(nameTd);
-
-      var referencesCell = Functions.createElement({
-        type: "td"
-      });
-      referencesCell.appendChild(self.getGuiUtils().createAnnotationList(annotations, {groupAnnotations: false}));
-
-      row.appendChild(referencesCell);
-
-      table.appendChild(row);
-      count++;
-    }
-  }
-
-  if (self.isOverlayFullView(overlay.getName()) && count === 0) {
-    row = document.createElement("tr");
-    cell = document.createElement("td");
-    cell.colSpan = 2;
-    cell.innerHTML = "No results available";
-    row.appendChild(cell);
-    table.appendChild(row);
-  }
-
-  if (!self.isOverlayFullView(overlay.getName()) && count === 0 && overlay.allowGeneralSearch()) {
-    row = document.createElement("tr");
-    cell = document.createElement("td");
-    cell.colSpan = 2;
-    cell.innerHTML = "Search for available targets";
-    row.appendChild(cell);
-    table.appendChild(row);
-  }
-  return table;
-};
-
-/**
- *
- * @returns {PromiseLike<any> | Promise<any>}
- */
-AbstractInfoWindow.prototype.init = function () {
-  var self = this;
-  var promises = [
-    // default settings of visualizing full information about elements
-    this.setOverlayFullView("drug", false),
-    this.setOverlayFullView("chemical", false),
-    this.setOverlayFullView("mirna", false),
-    this.setOverlayFullView("search", false),
-    // only all comments should be visible from the beginning
-    this.setOverlayFullView("comment", true)
-  ];
-
-  return Promise.all(promises).then(function () {
-    // listener called when user want to see all data about specific data overlay
-    var onOverlayFullViewChanged = function (e) {
-      var self = e.object;
-      // first change the content of the element
-      return self.update().then(function () {
-        if (e.newVal) {
-          var element = new IdentifiedElement({
-            objectId: self.getElementId(),
-            modelId: self.getCustomMap().getId(),
-            type: self.getElementType()
-          });
-          var topMap = self.getCustomMap().getTopMap();
-          return topMap.retrieveOverlayDetailDataForElement(element, self.getOverlayFullViewArray());
-        }
-      });
-
-    };
-
-    self.addPropertyChangeListener("overlayFullView", onOverlayFullViewChanged);
-
-    self._infoWindow = self.getCustomMap().getMapCanvas().createInfoWindow({
-      content: self.content,
-      position: self.getPosition(),
-      marker: self._marker
-    });
-
-    return ServerConnector.getConfiguration();
-  }).then(function (configuration) {
-    self.getGuiUtils().setConfiguration(configuration);
-    self.getGuiUtils().setMap(self.getCustomMap());
-  });
-};
-
-/**
- *
- * @param {GuiUtils} guiUtils
- */
-AbstractInfoWindow.prototype.setGuiUtils = function (guiUtils) {
-  this._guiUtils = guiUtils;
-};
-
-/**
- *
- * @returns {GuiUtils}
- */
-AbstractInfoWindow.prototype.getGuiUtils = function () {
-  return this._guiUtils;
-};
-
-/**
- * Creates and returns DOM div for chemical overlay information.
- *
- * @param {AbstractDbOverlay} overlay
- * @param {Chemical[]} data
- *          data taken from chemical overlay
- * @returns {HTMLElement} element with a div for comment overlay information
- * @private
- */
-AbstractInfoWindow.prototype._createChemicalInfoDiv = function (overlay, data) {
-  return this._createTargetInfoDiv({
-    overlay: overlay,
-    data: data,
-    name: "Interacting chemicals"
-  });
-};
-
-/**
- * Creates and returns DOM div for mi rna overlay information.
- *
- * @param {AbstractDbOverlay} overlay
- * @param {MiRna[]} data
- *          data taken from mi rna overlay
- * @returns {HTMLElement} DOM element with a div for comment overlay information
- */
-AbstractInfoWindow.prototype._createMiRnaInfoDiv = function (overlay, data) {
-  return this._createTargetInfoDiv({
-    overlay: overlay,
-    data: data,
-    name: "Interacting Micro RNAs"
-  });
-};
-
-/**
- *
- * @param {IdentifiedElement} identifiedElement
- */
-AbstractInfoWindow.prototype.setIdentifiedElement = function (identifiedElement) {
-  if (identifiedElement === undefined) {
-    throw new Error("identifiedElement cannot be undefined");
-  }
-  this._identifiedElement = identifiedElement;
-};
-
-/**
- *
- * @returns {IdentifiedElement}
- */
-AbstractInfoWindow.prototype.getIdentifiedElement = function () {
-  return this._identifiedElement;
-};
-
-
-/**
- * Method returning identifier of the object for which this window was created.
- *
- * @returns {string|number}
- */
-AbstractInfoWindow.prototype.getElementId = function () {
-  return this.getIdentifiedElement().getId();
-};
-
-/**
- * Method returning type of the object for which this window was created.
- *
- * @returns {string}
- */
-AbstractInfoWindow.prototype.getElementType = function () {
-  return this.getIdentifiedElement().getType();
-};
-
-/**
- * @returns {Point}
- */
-AbstractInfoWindow.prototype.getPosition = function () {
-  throw new Error("Not Implemented");
-};
-
-module.exports = AbstractInfoWindow;
+"use strict";
+
+var Promise = require("bluebird");
+
+var logger = require('../../logger');
+var Functions = require('../../Functions');
+
+var Comment = require('../data/Comment');
+var GuiConnector = require('../../GuiConnector');
+var GuiUtils = require('../../gui/leftPanel/GuiUtils');
+var IdentifiedElement = require('../data/IdentifiedElement');
+var ObjectWithListeners = require('../../ObjectWithListeners');
+var TargettingStructure = require('../data/TargettingStructure');
+
+/**
+ * Class representing any info window in our map.
+ *
+ * @param {IdentifiedElement} params.identifiedElement
+ * @param {AbstractCustomMap} params.map
+ * @param {Marker} params.marker
+ *
+ * @constructor
+ */
+function AbstractInfoWindow(params) {
+  // call super constructor
+  ObjectWithListeners.call(this);
+
+  var self = this;
+
+  self.setIdentifiedElement(params.identifiedElement);
+
+  self.setCustomMap(params.map);
+  self.setMarker(params.marker);
+
+  self.setContent(this.createWaitingContentDiv());
+
+  self._overlayFullView = [];
+
+  self.registerPropertyType("overlayFullView");
+
+  self.registerListenerType("onShow");
+  self.registerListenerType("onUpdate");
+
+  self.setGuiUtils(new GuiUtils());
+
+  var dbOverlaySearchChanged = function () {
+    return self.update();
+  };
+  var searchDbOverlay = params.map.getTopMap().getOverlayByName("search");
+  if (searchDbOverlay !== undefined) {
+    searchDbOverlay.addListener("onSearch", dbOverlaySearchChanged);
+  }
+  var commentDbOverlay = params.map.getTopMap().getOverlayByName("comment");
+  if (commentDbOverlay !== undefined) {
+    commentDbOverlay.addListener("onSearch", dbOverlaySearchChanged);
+  }
+}
+
+AbstractInfoWindow.prototype = Object.create(ObjectWithListeners.prototype);
+AbstractInfoWindow.prototype.constructor = AbstractInfoWindow;
+
+/**
+ * Returns <code>true</code> if overlay should visualize all possible values.
+ *
+ * @param {string} overlayName
+ *          name of the overlay
+ * @returns {boolean}, <code>true</code> if overlay should visualize all possible values
+ */
+AbstractInfoWindow.prototype.isOverlayFullView = function (overlayName) {
+  if (this._overlayFullView[overlayName] === undefined) {
+    this._overlayFullView[overlayName] = false;
+  }
+  return this._overlayFullView[overlayName];
+};
+
+/**
+ * Returns associative array with information if specific overlay should present
+ * all possible results or only specified by the data searched by user.
+ *
+ * @returns {Object.<string,boolean>} with information if specific overlay should present all
+ *          possible results or only specified by the data searched by user
+ */
+AbstractInfoWindow.prototype.getOverlayFullViewArray = function () {
+  return this._overlayFullView;
+};
+
+/**
+ *
+ * @param {string} overlayName
+ * @param {boolean} value
+ * @returns {Promise}
+ */
+AbstractInfoWindow.prototype.setOverlayFullView = function (overlayName, value) {
+  var oldVal = this._overlayFullView[overlayName];
+  this._overlayFullView[overlayName] = value;
+  if (oldVal !== value) {
+    return this.firePropertyChangeListener("overlayFullView", overlayName + "," + oldVal, value);
+  } else {
+    return Promise.resolve();
+  }
+};
+
+/**
+ * This method checks if {@link AbstractInfoWindow} is opened.
+ *
+ * @returns {Boolean} <code>true</code> if window is opened,
+ *          <code>false</code> otherwise
+ */
+AbstractInfoWindow.prototype.isOpened = function () {
+  if (this._infoWindow === undefined) {
+    return false;
+  }
+  return this._infoWindow.isOpened();
+};
+
+/**
+ * Opens Info Window.
+ *
+ * @param {Marker} [newMarker]
+ *
+ * @returns {PromiseLike<any> | Promise<any>}
+ */
+AbstractInfoWindow.prototype.open = function (newMarker) {
+  var self = this;
+  var infoWindow = self._infoWindow;
+  if (infoWindow === null || infoWindow === undefined) {
+    logger.warn("Cannot open window.");
+    return Promise.resolve();
+  }
+  if (newMarker !== undefined) {
+    infoWindow.setMarker(newMarker);
+  }
+  infoWindow.open();
+
+  return self.update().then(function () {
+    return self.callListeners("onShow");
+  });
+};
+
+/**
+ * Sets new content of the info window.
+ *
+ * @param {HTMLElement|string} content
+ *          new content of the window
+ */
+AbstractInfoWindow.prototype.setContent = function (content) {
+  var self = this;
+  self._content = content;
+  if (self._infoWindow !== undefined) {
+    self._infoWindow.setContent(content);
+  }
+};
+
+/**
+ * Returns content visualized in the info window.
+ *
+ * @returns {string|HTMLElement} content visualized in the info window
+ */
+AbstractInfoWindow.prototype.getContent = function () {
+  return this._content;
+};
+
+/**
+ * Creates div for an overlay data.
+ *
+ * @param {AbstractDbOverlay} overlay
+ *          corresponding overlay
+ * @param {BioEntity[]|Comment[]|Drug[]|MiRna[]|Chemical[]} data
+ *          data taken from overlay
+ * @returns {HTMLElement} div for given overlay data
+ */
+AbstractInfoWindow.prototype.createOverlayInfoDiv = function (overlay, data) {
+  var alias = this.alias;
+  if (alias !== undefined) {
+    if (alias.getType() !== undefined) {
+      if (overlay.name === "drug") {
+        if (alias.getType().toUpperCase() === "RNA" || //
+          alias.getType().toUpperCase() === "PROTEIN" || //
+          alias.getType().toUpperCase() === "GENE") {
+          return this._createDrugInfoDiv(overlay, data);
+        } else {
+          return null;
+        }
+      } else if (overlay.name === "chemical") {
+        if (this.alias.getType().toUpperCase() === "RNA" || //
+          alias.getType().toUpperCase() === "PROTEIN" || //
+          alias.getType().toUpperCase() === "GENE") {
+          return this._createChemicalInfoDiv(overlay, data);
+        } else {
+          return null;
+        }
+      } else if (overlay.name === "mirna") {
+        if (alias.getType().toUpperCase() === "RNA" || //
+          alias.getType().toUpperCase() === "PROTEIN" || //
+          alias.getType().toUpperCase() === "GENE") {
+          return this._createMiRnaInfoDiv(overlay, data);
+        } else {
+          return null;
+        }
+
+      } else if (overlay.name === "comment") {
+        return this._createCommentInfoDiv(overlay, data);
+      } else {
+        logger.warn("Unknown overlay data for AliasInfoWindow: " + overlay.name);
+        return this._createDefaultInfoDiv(overlay, data);
+      }
+    } else {
+      logger.debug(alias);
+      throw new Error("Cannot customize info window. Alias type is unknown ");
+    }
+  } else {
+    if (overlay.getName() === "comment") {
+      return this._createCommentInfoDiv(overlay, data);
+    } else {
+      logger.debug("Cannot customize info window. Alias not defined. Overlay: " + overlay.getName());
+      return null;
+    }
+  }
+};
+
+/**
+ * Creates and returns div for drug overlay information.
+ *
+ * @param {AbstractDbOverlay} overlay
+ * @param {Drug[]} data
+ *          data taken from drug overlay
+ * @returns {HTMLElement} div for drug overlay information
+ * @private
+ */
+AbstractInfoWindow.prototype._createDrugInfoDiv = function (overlay, data) {
+  return this._createTargetInfoDiv({
+    overlay: overlay,
+    data: data,
+    name: "Interacting drugs"
+  });
+};
+
+/**
+ * Creates and returns div for comment overlay information.
+ *
+ * @param {AbstractDbOverlay} overlay
+ * @param {Comment[]} data
+ *          data taken from comment overlay
+ * @returns {HTMLElement} div for comment overlay information
+ */
+AbstractInfoWindow.prototype._createCommentInfoDiv = function (overlay, data) {
+  if (data.length === 0 || data[0] === undefined) {
+    return null;
+  }
+  var result = document.createElement("div");
+
+  var titleElement = document.createElement("h3");
+  titleElement.innerHTML = "Comments";
+  result.appendChild(titleElement);
+  for (var i = 0; i < data.length; i++) {
+    var comment = data[i];
+    if (comment instanceof Comment) {
+      if (!comment.isRemoved()) {
+        result.appendChild(document.createElement("hr"));
+        var commentId = document.createElement("div");
+        commentId.innerHTML = '#' + comment.getId();
+        result.appendChild(commentId);
+        result.appendChild(document.createElement("br"));
+        var commentContent = Functions.createElement({type: "div", content: comment.getContent(), xss: true});
+
+        result.appendChild(commentContent);
+      }
+    } else {
+      throw new Error("Invalid comment data: " + comment);
+    }
+  }
+
+  return result;
+};
+
+/**
+ * Creates and returns div for unknown overlay.
+ *
+ * @param {AbstractDbOverlay} overlay
+ * @param {Array} data
+ *          data taken from overlay
+ * @returns {HTMLElement} div for overlay information
+ */
+
+AbstractInfoWindow.prototype._createDefaultInfoDiv = function (overlay, data) {
+  var divElement = document.createElement("div");
+  var count = 0;
+
+  var titleElement = document.createElement("h3");
+  var title = document.createTextNode(overlay.getName());
+  titleElement.appendChild(title);
+  divElement.appendChild(titleElement);
+  for (var searchId in data) {
+    if (data.hasOwnProperty(searchId) && data[searchId] !== undefined && data[searchId] !== null) {
+      count++;
+      var resultTitleElement = document.createElement("h4");
+      var resultTitle = document.createTextNode(searchId);
+      resultTitleElement.appendChild(resultTitle);
+      divElement.appendChild(resultTitleElement);
+
+      var keys = Object.keys(data[searchId]);
+      for (var i = 0; i < keys.length; i++) {
+        var resultValElement = document.createElement("p");
+        var resultVal = document.createTextNode(keys[i] + ": " + data[searchId][keys[i]]);
+        resultValElement.appendChild(resultVal);
+        divElement.appendChild(resultValElement);
+      }
+    }
+  }
+
+  if (count === 0) {
+    divElement = null;
+  }
+  return divElement;
+};
+
+/**
+ * Returns Marker object where this info window is attached.
+ *
+ * @returns {Marker} object where this info window is attached
+ */
+AbstractInfoWindow.prototype.getMarker = function () {
+  return this._marker;
+};
+
+/**
+ *
+ * @param {Marker} marker
+ */
+AbstractInfoWindow.prototype.setMarker = function (marker) {
+  this._marker = marker;
+  if (this._infoWindow !== undefined) {
+    this._infoWindow.setMarker(marker);
+  }
+};
+
+/**
+ * Returns {@link AbstractCustomMap} where this window is presented.
+ *
+ * @returns {AbstractCustomMap} where this window is presented
+ */
+AbstractInfoWindow.prototype.getCustomMap = function () {
+  return this.customMap;
+};
+
+/**
+ *
+ * @param {AbstractCustomMap} map
+ */
+AbstractInfoWindow.prototype.setCustomMap = function (map) {
+  if (map === undefined) {
+    throw new Error("Map must be defined");
+  }
+  this.customMap = map;
+};
+
+/**
+ * Returns html DOM object with content that should presented when waiting for
+ * some data from server.
+ *
+ * @returns {HTMLElement} html with content that should presented when waiting for
+ *          some data from server
+ */
+AbstractInfoWindow.prototype.createWaitingContentDiv = function () {
+  var result = document.createElement("div");
+  var img = document.createElement("img");
+  img.src = GuiConnector.getImgPrefix() + GuiConnector.getLoadingImg();
+  var message = document.createElement("h4");
+  message.innerHTML = "loading...";
+  result.appendChild(img);
+  result.appendChild(message);
+  return result;
+};
+
+/**
+ * This is a generic method that updates content of the window.
+ *
+ * @returns {Promise|PromiseLike}
+ * @private
+ */
+AbstractInfoWindow.prototype._updateContent = function () {
+  var contentDiv = null;
+  var self = this;
+
+  if (!self.isOpened()) {
+    return Promise.resolve();
+  } else {
+    self.setContent(self.createWaitingContentDiv());
+
+    return self.createContentDiv().then(function (content) {
+      contentDiv = content;
+      return self.createDbOverlaysDiv();
+    }).then(function (overlaysDiv) {
+      if (overlaysDiv !== undefined && overlaysDiv !== null) {
+        contentDiv.appendChild(overlaysDiv);
+      }
+      self.setContent(contentDiv);
+      return self.callListeners("onUpdate");
+    }).then(function () {
+      return contentDiv;
+    });
+  }
+};
+
+/**
+ * Creates and returns div with overlays content.
+ *
+ * @returns {Promise<HTMLElement>} with html representing data taken from
+ *          {@link AbstractDbOverlay} for this window
+ */
+AbstractInfoWindow.prototype.createDbOverlaysDiv = function () {
+  var self = this;
+  var result = document.createElement("div");
+  return this.getDbOverlaysData(self.getOverlayFullViewArray()).then(function (overlayData) {
+    for (var i = 0; i < overlayData.length; i++) {
+      var overlay = overlayData[i].overlay;
+      var data = overlayData[i].data;
+      var overlayInfo = self.createOverlayInfoDiv(overlay, data);
+      if (overlayInfo !== null) {
+        result.appendChild(overlayInfo);
+      }
+    }
+    return result;
+  });
+};
+
+// noinspection JSUnusedLocalSymbols
+/**
+ * Returns array with data taken from all known {@link AbstractDbOverlay}.
+ *
+ * @param {Object.<string,boolean>} general
+ *          if true then all elements will be returned, if false then only ones
+ *          available right now in the overlay
+ *
+ * @returns {Promise} array with data from {@link AbstractDbOverlay}
+ */
+AbstractInfoWindow.prototype.getDbOverlaysData = function (general) {
+  throw new Error("Not implemented");
+};
+
+/**
+ * Abstract method (to be implemented by subclasses) for updating content.
+ *
+ * @returns {Promise}
+ */
+AbstractInfoWindow.prototype.update = function () {
+  return this._updateContent();
+};
+
+/**
+ *
+ * @param {string} params.name
+ * @param {AbstractDbOverlay} params.overlay
+ * @param {Array} params.data
+ * @returns {HTMLElement}
+ * @private
+ */
+AbstractInfoWindow.prototype._createTargetInfoDiv = function (params) {
+  var overlay = params.overlay;
+  var data = params.data;
+  var name = params.name;
+
+  var self = this;
+  var result = document.createElement("div");
+
+  var titleElement = document.createElement("h3");
+  titleElement.innerHTML = name;
+  result.appendChild(titleElement);
+  if (overlay.allowGeneralSearch()) {
+    var checkboxDiv = document.createElement("div");
+    checkboxDiv.style.float = "right";
+
+    var checkbox = document.createElement("input");
+    checkbox.id = "checkbox-" + name + "-" + this.getElementType() + "-" + this.getElementId();
+    checkbox.type = "checkbox";
+    checkbox.checked = self.isOverlayFullView(overlay.getName());
+    checkbox.onclick = function () {
+      return self.setOverlayFullView(overlay.getName(), this.checked).then(null, GuiConnector.alert);
+    };
+
+    checkboxDiv.appendChild(checkbox);
+
+    var description = document.createElement("div");
+    description.style.float = "right";
+    description.innerHTML = "Show all";
+    checkboxDiv.appendChild(description);
+    result.appendChild(checkboxDiv);
+  }
+  var count = 0;
+  for (var dataId in data) {
+    if (data.hasOwnProperty(dataId)) {
+      count++;
+    }
+  }
+
+  var table = self._createTableForTargetDiv(data, overlay);
+
+  if (count === 0 && !overlay.allowGeneralSearch() && !this.isOverlayFullView(overlay.getName())) {
+    result = null;
+  }
+  if (result !== null) {
+    result.appendChild(table);
+  }
+  return result;
+};
+
+/**
+ *
+ * @param {Array} data
+ * @param {AbstractDbOverlay} overlay
+ * @returns {HTMLElement}
+ * @private
+ */
+AbstractInfoWindow.prototype._createTableForTargetDiv = function (data, overlay) {
+  var self = this;
+  var table = document.createElement("table");
+  table.className = "minerva-window-drug-table";
+  var header = document.createElement("tr");
+  var headerCol = document.createElement("th");
+  headerCol.innerHTML = "Name";
+  header.appendChild(headerCol);
+  headerCol = document.createElement("th");
+  headerCol.innerHTML = "References";
+  header.appendChild(headerCol);
+
+  var cell;
+  table.appendChild(header);
+  var row;
+
+  var onclick = function () {
+    // ';' enforces single query (in case there are ',' characters in the name)
+    return overlay.searchByQuery(this.innerHTML + ";");
+  };
+
+  var count = 0;
+  for (var searchId in data) {
+    if (data.hasOwnProperty(searchId)) {
+
+      row = document.createElement("tr");
+      var nameContent = searchId;
+      var annotations = [];
+      if (typeof data[searchId] === "string") {
+        nameContent = data[searchId];
+      } else if (data[searchId] instanceof TargettingStructure) {
+        nameContent = data[searchId].getName();
+        var targets = data[searchId].getTargetsForIdentifiedElement(self.getIdentifiedElement());
+        for (var i = 0; i < targets.length; i++) {
+          var references = targets[i].getReferences();
+          for (var j = 0; j < references.length; j++) {
+            annotations.push(references[j]);
+          }
+        }
+      }
+      var link = Functions.createElement({
+        type: "a",
+        onclick: onclick,
+        href: "#",
+        content: nameContent
+      });
+
+      var nameTd = Functions.createElement({
+        type: "td"
+      });
+      nameTd.appendChild(link);
+      row.appendChild(nameTd);
+
+      var referencesCell = Functions.createElement({
+        type: "td"
+      });
+      referencesCell.appendChild(self.getGuiUtils().createAnnotationList(annotations, {groupAnnotations: false}));
+
+      row.appendChild(referencesCell);
+
+      table.appendChild(row);
+      count++;
+    }
+  }
+
+  if (self.isOverlayFullView(overlay.getName()) && count === 0) {
+    row = document.createElement("tr");
+    cell = document.createElement("td");
+    cell.colSpan = 2;
+    cell.innerHTML = "No results available";
+    row.appendChild(cell);
+    table.appendChild(row);
+  }
+
+  if (!self.isOverlayFullView(overlay.getName()) && count === 0 && overlay.allowGeneralSearch()) {
+    row = document.createElement("tr");
+    cell = document.createElement("td");
+    cell.colSpan = 2;
+    cell.innerHTML = "Search for available targets";
+    row.appendChild(cell);
+    table.appendChild(row);
+  }
+  return table;
+};
+
+/**
+ *
+ * @returns {PromiseLike<any> | Promise<any>}
+ */
+AbstractInfoWindow.prototype.init = function () {
+  var self = this;
+  var promises = [
+    // default settings of visualizing full information about elements
+    this.setOverlayFullView("drug", false),
+    this.setOverlayFullView("chemical", false),
+    this.setOverlayFullView("mirna", false),
+    this.setOverlayFullView("search", false),
+    // only all comments should be visible from the beginning
+    this.setOverlayFullView("comment", true)
+  ];
+
+  return Promise.all(promises).then(function () {
+    // listener called when user want to see all data about specific data overlay
+    var onOverlayFullViewChanged = function (e) {
+      var self = e.object;
+      // first change the content of the element
+      return self.update().then(function () {
+        if (e.newVal) {
+          var element = new IdentifiedElement({
+            objectId: self.getElementId(),
+            modelId: self.getCustomMap().getId(),
+            type: self.getElementType()
+          });
+          var topMap = self.getCustomMap().getTopMap();
+          return topMap.retrieveOverlayDetailDataForElement(element, self.getOverlayFullViewArray());
+        }
+      });
+
+    };
+
+    self.addPropertyChangeListener("overlayFullView", onOverlayFullViewChanged);
+
+    self._infoWindow = self.getCustomMap().getMapCanvas().createInfoWindow({
+      content: self.content,
+      position: self.getPosition(),
+      marker: self._marker
+    });
+
+    return ServerConnector.getConfiguration();
+  }).then(function (configuration) {
+    self.getGuiUtils().setConfiguration(configuration);
+    self.getGuiUtils().setMap(self.getCustomMap());
+  });
+};
+
+/**
+ *
+ * @param {GuiUtils} guiUtils
+ */
+AbstractInfoWindow.prototype.setGuiUtils = function (guiUtils) {
+  this._guiUtils = guiUtils;
+};
+
+/**
+ *
+ * @returns {GuiUtils}
+ */
+AbstractInfoWindow.prototype.getGuiUtils = function () {
+  return this._guiUtils;
+};
+
+/**
+ * Creates and returns DOM div for chemical overlay information.
+ *
+ * @param {AbstractDbOverlay} overlay
+ * @param {Chemical[]} data
+ *          data taken from chemical overlay
+ * @returns {HTMLElement} element with a div for comment overlay information
+ * @private
+ */
+AbstractInfoWindow.prototype._createChemicalInfoDiv = function (overlay, data) {
+  return this._createTargetInfoDiv({
+    overlay: overlay,
+    data: data,
+    name: "Interacting chemicals"
+  });
+};
+
+/**
+ * Creates and returns DOM div for mi rna overlay information.
+ *
+ * @param {AbstractDbOverlay} overlay
+ * @param {MiRna[]} data
+ *          data taken from mi rna overlay
+ * @returns {HTMLElement} DOM element with a div for comment overlay information
+ */
+AbstractInfoWindow.prototype._createMiRnaInfoDiv = function (overlay, data) {
+  return this._createTargetInfoDiv({
+    overlay: overlay,
+    data: data,
+    name: "Interacting Micro RNAs"
+  });
+};
+
+/**
+ *
+ * @param {IdentifiedElement} identifiedElement
+ */
+AbstractInfoWindow.prototype.setIdentifiedElement = function (identifiedElement) {
+  if (identifiedElement === undefined) {
+    throw new Error("identifiedElement cannot be undefined");
+  }
+  this._identifiedElement = identifiedElement;
+};
+
+/**
+ *
+ * @returns {IdentifiedElement}
+ */
+AbstractInfoWindow.prototype.getIdentifiedElement = function () {
+  return this._identifiedElement;
+};
+
+
+/**
+ * Method returning identifier of the object for which this window was created.
+ *
+ * @returns {string|number}
+ */
+AbstractInfoWindow.prototype.getElementId = function () {
+  return this.getIdentifiedElement().getId();
+};
+
+/**
+ * Method returning type of the object for which this window was created.
+ *
+ * @returns {string}
+ */
+AbstractInfoWindow.prototype.getElementType = function () {
+  return this.getIdentifiedElement().getType();
+};
+
+/**
+ * @returns {Point}
+ */
+AbstractInfoWindow.prototype.getPosition = function () {
+  throw new Error("Not Implemented");
+};
+
+module.exports = AbstractInfoWindow;
diff --git a/frontend-js/src/main/js/map/window/AliasInfoWindow.js b/frontend-js/src/main/js/map/window/AliasInfoWindow.js
index 0bc086e396a5753b2a0561401985f55f0ee1ec01..f80f8bfc7eeb1635491e921e36b5f9c8329ae146 100644
--- a/frontend-js/src/main/js/map/window/AliasInfoWindow.js
+++ b/frontend-js/src/main/js/map/window/AliasInfoWindow.js
@@ -36,10 +36,7 @@ function AliasInfoWindow(params) {
   this.setAlias(params.alias);
 
   var overlayListChanged = function () {
-    return self.getCustomMap().getAliasVisibleLayoutsData(self.getAlias().getId()).then(function (layoutAliases) {
-      self.layoutAliases = layoutAliases;
-      return self.update();
-    });
+    return self.update();
   };
 
   var dbOverlaySearchChanged = function () {
@@ -97,99 +94,147 @@ AliasInfoWindow.prototype.init = function () {
  * Creates and returns chart representing data related to alias on different
  * overlays.
  *
+ * @param {DataOverlay[]} params.overlays
+ *
  * @returns {PromiseLike<HTMLElement>} html element representing chart with data related to alias
  *          on different overlays
  */
-AliasInfoWindow.prototype.createChartDiv = function () {
+AliasInfoWindow.prototype.createChartDiv = function (params) {
+  var overlays = params.overlays;
+
   var result = document.createElement("div");
-  var rows = [];
+  var promises = [];
   var self = this;
-  return Promise.each(self.layoutAliases, function (data, i) {
-    var rowDiv = document.createElement("div");
-    if (i % 2 === 0) {
-      rowDiv.className = "mapChartRowEvenDiv";
-    } else {
-      rowDiv.className = "mapChartRowOddDiv";
-    }
-    rowDiv.style.position = "relative";
-    var nameDiv = document.createElement("div");
-    nameDiv.className = "mapChartNameDiv";
-    nameDiv.innerHTML = self.layoutNames[i] + "&nbsp;";
-    rowDiv.appendChild(nameDiv);
-
-    rows[i] = rowDiv;
-    if (data !== undefined && data !== null) {
-      return Functions.overlayToColor(data).then(function (color) {
-        var value = parseFloat(data.value);
-        var description = data.description;
-        if (description === null || description === undefined || description === "") {
-          description = "";
-          if (!isNaN(value)) {
-            description = value.toFixed(2);
+
+  overlays.forEach(function (overlay, i) {
+    promises.push(overlay.getFullAliasById(self.getAlias().getId()).then(function (data) {
+      var rowDiv = document.createElement("div");
+      if (i % 2 === 0 || self.getAlias().getLinkedSubmodelId() !== undefined) {
+        rowDiv.className = "minerva-chart-row-even";
+      } else {
+        rowDiv.className = "minerva-chart-row-odd";
+      }
+      var nameDiv = document.createElement("div");
+      nameDiv.className = "minerva-chart-name";
+      nameDiv.innerHTML = overlays[i].getName() + "&nbsp;";
+      rowDiv.appendChild(nameDiv);
+
+      if (data !== undefined && data !== null) {
+        return Functions.overlayToColor(data).then(function (color) {
+          var value = parseFloat(data.value);
+          var description = data.description;
+          if (description === null || description === undefined || description === "") {
+            description = "";
+            if (!isNaN(value)) {
+              description = value.toFixed(2);
+            }
           }
-        }
-        var leftMarginDiv = document.createElement("div");
-        leftMarginDiv.innerHTML = "&nbsp;";
-        leftMarginDiv.style.float = "left";
-        var centerBarDiv = document.createElement("div");
-        centerBarDiv.style.width = "1px";
-        centerBarDiv.style.float = "left";
-        centerBarDiv.style.background = "#000000";
-        centerBarDiv.innerHTML = "&nbsp;";
-
-        var rightBarDiv = document.createElement("div");
-        rightBarDiv.innerHTML = "&nbsp;";
-        rightBarDiv.style.float = "left";
-        rightBarDiv.style.background = color;
-        rightBarDiv.style.width = Math.abs(value * 100) + "px";
-        var offset = 100;
-        var descDiv = document.createElement("div");
-        descDiv.style.float = "right";
-        descDiv.style.textAlign = "right";
-        descDiv.style.position = "absolute";
-        descDiv.style.right = "0";
-        descDiv.innerHTML = "<span>" + description + "</span>";
-        if (!isNaN(value)) {
-          if (value > 0) {
-            offset = 100;
-            leftMarginDiv.style.width = offset + "px";
+          var leftMarginDiv = document.createElement("div");
+          leftMarginDiv.innerHTML = "&nbsp;";
+          leftMarginDiv.style.float = "left";
+          var centerBarDiv = document.createElement("div");
+          centerBarDiv.style.width = "1px";
+          centerBarDiv.style.float = "left";
+          centerBarDiv.style.background = "#000000";
+          centerBarDiv.innerHTML = "&nbsp;";
+
+          var rightBarDiv = document.createElement("div");
+          rightBarDiv.innerHTML = "&nbsp;";
+          rightBarDiv.style.float = "left";
+          rightBarDiv.style.background = color;
+          rightBarDiv.style.width = Math.abs(value * 100) + "px";
+          var offset = 100;
+          var descDiv = document.createElement("div");
+          descDiv.style.float = "right";
+          descDiv.style.textAlign = "right";
+          descDiv.style.position = "absolute";
+          descDiv.style.right = "0";
+          descDiv.innerHTML = "<span>" + description + "</span>";
+          if (!isNaN(value)) {
+            if (value > 0) {
+              offset = 100;
+              leftMarginDiv.style.width = offset + "px";
 
-            rightBarDiv.style.textAlign = "right";
+              rightBarDiv.style.textAlign = "right";
+
+              rowDiv.appendChild(leftMarginDiv);
+              rowDiv.appendChild(centerBarDiv);
+              rowDiv.appendChild(rightBarDiv);
+            } else {
+              offset = 100 + (value * 100);
+              leftMarginDiv.style.width = offset + "px";
+
+              rowDiv.appendChild(leftMarginDiv);
+              rowDiv.appendChild(rightBarDiv);
+              rowDiv.appendChild(centerBarDiv);
+            }
 
-            rowDiv.appendChild(leftMarginDiv);
-            rowDiv.appendChild(centerBarDiv);
-            rowDiv.appendChild(rightBarDiv);
           } else {
-            offset = 100 + (value * 100);
+            offset = 100;
             leftMarginDiv.style.width = offset + "px";
-
+            leftMarginDiv.style.background = color;
+            rightBarDiv.style.width = offset + "px";
+            rightBarDiv.style.background = color;
+            rightBarDiv.style.textAlign = "right";
             rowDiv.appendChild(leftMarginDiv);
-            rowDiv.appendChild(rightBarDiv);
             rowDiv.appendChild(centerBarDiv);
+            rowDiv.appendChild(rightBarDiv);
+          }
+          rowDiv.appendChild(descDiv);
+          return rowDiv;
+        });
+      } else {
+        var emptyDiv = document.createElement("div");
+        emptyDiv.innerHTML = "&nbsp;";
+        emptyDiv.style.float = "left";
+        emptyDiv.style.width = "201px";
+        rowDiv.appendChild(emptyDiv);
+        return rowDiv;
+      }
+    }));
+    if (self.getAlias().getLinkedSubmodelId() !== undefined) {
+      promises.push(new Promise.resolve(overlay).then(function (overlay) {
+        var overlayData = overlay.getAliases();
+        var overlayDataOnSubmap = [];
+        for (var j = 0; j < overlayData.length; j++) {
+          var data = overlayData[j];
+          if (data.getModelId() === self.getAlias().getLinkedSubmodelId()) {
+            overlayDataOnSubmap.push(data);
           }
-
-        } else {
-          offset = 100;
-          leftMarginDiv.style.width = offset + "px";
-          leftMarginDiv.style.background = color;
-          rightBarDiv.style.width = offset + "px";
-          rightBarDiv.style.background = color;
-          rightBarDiv.style.textAlign = "right";
-          rowDiv.appendChild(leftMarginDiv);
-          rowDiv.appendChild(centerBarDiv);
-          rowDiv.appendChild(rightBarDiv);
         }
-        rowDiv.appendChild(descDiv);
-      });
-    } else {
-      var emptyDiv = document.createElement("div");
-      emptyDiv.innerHTML = "&nbsp;";
-      emptyDiv.style.float = "left";
-      emptyDiv.style.width = "201px";
-      rowDiv.appendChild(emptyDiv);
-      return Promise.resolve();
+        return Functions.overlaysToColorDataStructure(overlayDataOnSubmap).then(function (colors) {
+          var rowDiv = document.createElement("div");
+          rowDiv.className = "minerva-chart-row-odd";
+          var nameDiv = document.createElement("div");
+          nameDiv.className = "minerva-chart-name";
+          var submapName = self.getCustomMap().getTopMap().getSubmapById(self.getAlias().getLinkedSubmodelId()).getModel().getName();
+          nameDiv.innerHTML = "Submap " + submapName;
+          rowDiv.appendChild(nameDiv);
+
+          var emptyDiv = document.createElement("div");
+          emptyDiv.style.float = "left";
+          emptyDiv.style.width = "201px";
+          rowDiv.appendChild(emptyDiv);
+
+          var totalAmount = 0;
+          for (var j = 0; j < colors.length; j++) {
+            totalAmount += colors[j].amount;
+          }
+
+          for (j = 0; j < colors.length; j++) {
+            var overlayDataDiv = document.createElement("div");
+            overlayDataDiv.innerHTML = "&nbsp;";
+            overlayDataDiv.style.float = "left";
+            overlayDataDiv.style.width = Math.floor(100*colors[j].amount / totalAmount) + "%";
+            overlayDataDiv.style.background = colors[j].color;
+            emptyDiv.appendChild(overlayDataDiv);
+          }
+          return rowDiv;
+        });
+      }));
     }
-  }).then(function () {
+  });
+  return Promise.all(promises).then(function (rows) {
     for (var i = 0; i < rows.length; i++) {
       result.appendChild(rows[i]);
     }
@@ -215,18 +260,14 @@ AliasInfoWindow.prototype.createContentDiv = function () {
 
     result.appendChild(overlayDiv);
 
-    return self.getCustomMap().getAliasVisibleLayoutsData(alias.getId()).then(function (layoutAliases) {
-      self.layoutAliases = layoutAliases;
-      return self.getCustomMap().getTopMap().getVisibleDataOverlays();
-    }).then(function (dataOverlays) {
-      self.layoutNames = [];
-      for (var i = 0; i < dataOverlays.length; i++) {
-        self.layoutNames.push(dataOverlays[i].getName());
-      }
-      return self.createChartDiv();
+    var overlays;
+
+    return self.getCustomMap().getTopMap().getVisibleDataOverlays().then(function (dataOverlays) {
+      overlays = dataOverlays;
+      return self.createChartDiv({overlays: overlays});
     }).then(function (chartDiv) {
       overlayDiv.appendChild(chartDiv);
-      return self.createGenomicDiv();
+      return self.createGenomicDiv({overlays: overlays});
     }).then(function (genomicDiv) {
       overlayDiv.appendChild(genomicDiv);
       return result;
@@ -245,15 +286,19 @@ AliasInfoWindow.prototype.createContentDiv = function () {
  *
  * @returns {Promise} array with data from {@link AbstractDbOverlay}
  */
-AliasInfoWindow.prototype.getOverlaysData = function (general) {
+AliasInfoWindow.prototype.getDbOverlaysData = function (general) {
   return this.getCustomMap().getTopMap().getOverlayDataForAlias(this.getAlias(), general);
 };
 
 /**
  *
- * @returns {PromiseLike<HTMLElement>}
+ * @param {DataOverlay[]} params.overlays
+ *
+ * @returns {Promise|PromiseLike}
  */
-AliasInfoWindow.prototype.createGenomicDiv = function () {
+AliasInfoWindow.prototype.createGenomicDiv = function (params) {
+  var overlays = params.overlays;
+
   var self = this;
 
   var result = document.createElement("div");
@@ -285,34 +330,41 @@ AliasInfoWindow.prototype.createGenomicDiv = function () {
 
   var globalGeneVariants = [];
 
-  return Promise.each(
-    self.layoutAliases,
-    function (data) {
+  var promises = [];
+  var overlaysData = [];
+
+  overlays.forEach(function (overlay) {
+    promises.push(overlay.getFullAliasById(self.getAlias().getId()));
+  });
+  return Promise.all(promises).then(function (result) {
+    promises = [];
+    overlaysData = result;
+    for (var i = 0; i < overlaysData.length; i++) {
+      var data = overlaysData[i];
       if (data !== null && data !== undefined && data.getType() === LayoutAlias.GENETIC_VARIANT) {
         geneticInformation = true;
-        return Promise.each(data.getGeneVariants(), function (variant) {
+        promises.push(Promise.each(data.getGeneVariants(), function (variant) {
           return self.getCustomMap().getTopMap().getReferenceGenome(variant.getReferenceGenomeType(),
-            variant.getReferenceGenomeVersion()).then(
-            function (genome) {
-              if (genome.getUrl() !== null && genome.getUrl() !== undefined) {
-                if (genomes[genome.getUrl()] === undefined) {
-                  genomes[genome.getUrl()] = genome;
-                  genomeUrls.push(genome.getUrl());
-                }
-              } else {
-                logger.warn("Genome for " + variant.getReferenceGenomeType() + ","
-                  + variant.getReferenceGenomeVersion() + " not loaded");
+            variant.getReferenceGenomeVersion()).then(function (genome) {
+            if (genome.getUrl() !== null && genome.getUrl() !== undefined) {
+              if (genomes[genome.getUrl()] === undefined) {
+                genomes[genome.getUrl()] = genome;
+                genomeUrls.push(genome.getUrl());
               }
-            },
-            function () {
+            } else {
               logger.warn("Genome for " + variant.getReferenceGenomeType() + ","
                 + variant.getReferenceGenomeVersion() + " not loaded");
+            }
+          }, function () {
+            logger.warn("Genome for " + variant.getReferenceGenomeType() + ","
+              + variant.getReferenceGenomeVersion() + " not loaded");
 
-            });
-        });
-
+          });
+        }));
       }
-    }).then(function () {
+    }
+    return Promise.all(promises);
+  }).then(function () {
     for (var i = 0; i < genomeUrls.length; i++) {
       var genome = genomes[genomeUrls[i]];
       pileupSource.splice(0, 0, {
@@ -334,7 +386,8 @@ AliasInfoWindow.prototype.createGenomicDiv = function () {
         });
       }
     }
-    return Promise.each(self.layoutAliases, function (data, i) {
+    for (i = 0; i < overlaysData.length; i++) {
+      var data = overlaysData[i];
       globalGeneVariants[i] = [];
       if (data !== null && data !== undefined && data.getType() === LayoutAlias.GENETIC_VARIANT) {
         var geneVariants = data.getGeneVariants();
@@ -349,13 +402,12 @@ AliasInfoWindow.prototype.createGenomicDiv = function () {
           pileupRange.stop = Math.max(pileupRange.stop, variant.getPosition() + length);
         }
       }
-    });
-  }).then(function () {
+    }
     if (geneticInformation) {
       if (genomeUrls.length === 0) {
         contentElement.innerHTML = "No reference genome data available on minerva platform";
       } else {
-        for (var i = 0; i < self.layoutAliases.length; i++) {
+        for (i = 0; i < overlaysData.length; i++) {
           if (globalGeneVariants[i].length > 0) {
             var vcfContent = self.createVcfString(globalGeneVariants[i]);
             pileupSource.push({
@@ -363,7 +415,7 @@ AliasInfoWindow.prototype.createGenomicDiv = function () {
               data: pileup.formats.vcf({
                 content: vcfContent
               }),
-              name: self.layoutNames[i] + ' - Variants',
+              name: overlays[i].getName() + ' - Variants',
               options: {
                 variantHeightByFrequency: true
               }
@@ -493,5 +545,4 @@ AliasInfoWindow.prototype.getPosition = function () {
   return new Point(alias.x + alias.width / 2, alias.y + alias.height / 2);
 };
 
-
 module.exports = AliasInfoWindow;
diff --git a/frontend-js/src/main/js/map/window/PointInfoWindow.js b/frontend-js/src/main/js/map/window/PointInfoWindow.js
index dc1c2d7f8bb90b04f783ce2ebd63acbad6f380ba..bf263c8e7a751e2e94ac1497e077cc2614c78f02 100644
--- a/frontend-js/src/main/js/map/window/PointInfoWindow.js
+++ b/frontend-js/src/main/js/map/window/PointInfoWindow.js
@@ -1,63 +1,63 @@
-"use strict";
-
-var Promise = require("bluebird");
-
-var AbstractInfoWindow = require('./AbstractInfoWindow');
-var IdentifiedElement = require('../data/IdentifiedElement');
-
-/**
- *
- * @param {IdentifiedElement} [params.identifiedElement]
- * @param {AbstractCustomMap} params.map
- * @param {Marker} params.marker
- * @param {PointData} params.point
- *
- * @constructor
- * @extends AbstractInfoWindow
- */
-function PointInfoWindow(params) {
-  if (params.identifiedElement === undefined) {
-    params.identifiedElement = new IdentifiedElement(params.point);
-  }
-  // call super constructor
-  AbstractInfoWindow.call(this, params);
-
-  this.pointData = params.point;
-
-}
-
-PointInfoWindow.prototype = Object.create(AbstractInfoWindow.prototype);
-PointInfoWindow.prototype.constructor = PointInfoWindow;
-
-/**
- *
- * @returns {PromiseLike<HTMLElement>}
- */
-PointInfoWindow.prototype.createContentDiv = function () {
-  var result = document.createElement("div");
-  var title = document.createElement("h3");
-  title.innerHTML = "Point: " + this.pointData.getPoint();
-  result.appendChild(title);
-
-  return Promise.resolve(result);
-};
-
-/**
- *
- * @param {Object.<string,boolean>} general
- * @returns {Promise}
- */
-PointInfoWindow.prototype.getOverlaysData = function (general) {
-  return this.getCustomMap().getTopMap().getOverlayDataForPoint(this.pointData, general);
-};
-
-/**
- *
- * @returns {Point}
- */
-PointInfoWindow.prototype.getPosition = function () {
-  return this.pointData.getPoint();
-};
-
-
-module.exports = PointInfoWindow;
+"use strict";
+
+var Promise = require("bluebird");
+
+var AbstractInfoWindow = require('./AbstractInfoWindow');
+var IdentifiedElement = require('../data/IdentifiedElement');
+
+/**
+ *
+ * @param {IdentifiedElement} [params.identifiedElement]
+ * @param {AbstractCustomMap} params.map
+ * @param {Marker} params.marker
+ * @param {PointData} params.point
+ *
+ * @constructor
+ * @extends AbstractInfoWindow
+ */
+function PointInfoWindow(params) {
+  if (params.identifiedElement === undefined) {
+    params.identifiedElement = new IdentifiedElement(params.point);
+  }
+  // call super constructor
+  AbstractInfoWindow.call(this, params);
+
+  this.pointData = params.point;
+
+}
+
+PointInfoWindow.prototype = Object.create(AbstractInfoWindow.prototype);
+PointInfoWindow.prototype.constructor = PointInfoWindow;
+
+/**
+ *
+ * @returns {PromiseLike<HTMLElement>}
+ */
+PointInfoWindow.prototype.createContentDiv = function () {
+  var result = document.createElement("div");
+  var title = document.createElement("h3");
+  title.innerHTML = "Point: " + this.pointData.getPoint();
+  result.appendChild(title);
+
+  return Promise.resolve(result);
+};
+
+/**
+ *
+ * @param {Object.<string,boolean>} general
+ * @returns {Promise}
+ */
+PointInfoWindow.prototype.getDbOverlaysData = function (general) {
+  return this.getCustomMap().getTopMap().getOverlayDataForPoint(this.pointData, general);
+};
+
+/**
+ *
+ * @returns {Point}
+ */
+PointInfoWindow.prototype.getPosition = function () {
+  return this.pointData.getPoint();
+};
+
+
+module.exports = PointInfoWindow;
diff --git a/frontend-js/src/main/js/map/window/ReactionInfoWindow.js b/frontend-js/src/main/js/map/window/ReactionInfoWindow.js
index cca0f9d2c820e3e6b6ebe8ed31a42415f8ffc07d..bd6b7c493c9be2b3b0d3539a3829c76b95bd7db0 100644
--- a/frontend-js/src/main/js/map/window/ReactionInfoWindow.js
+++ b/frontend-js/src/main/js/map/window/ReactionInfoWindow.js
@@ -1,264 +1,264 @@
-"use strict";
-
-var Promise = require("bluebird");
-
-var AbstractInfoWindow = require('./AbstractInfoWindow');
-var GuiUtils = require('../../gui/leftPanel/GuiUtils');
-var IdentifiedElement = require('../data/IdentifiedElement');
-var Reaction = require('../data/Reaction');
-var Functions = require('../../Functions');
-var logger = require('../../logger');
-
-/**
- * Class representing info window that should be opened when clicking on
- * reaction.
- */
-/**
- *
- * @param {IdentifiedElement} [params.identifiedElement]
- * @param {AbstractCustomMap} params.map
- * @param {Marker} params.marker
- * @param {Reaction} params.reaction
- *
- * @constructor
- * @extends AbstractInfoWindow
- */
-function ReactionInfoWindow(params) {
-  if (params.identifiedElement === undefined) {
-    params.identifiedElement = new IdentifiedElement(params.reaction);
-  }
-  // call super constructor
-  AbstractInfoWindow.call(this, params);
-
-  var self = this;
-
-  self.setReactionData(params.reaction);
-  if (params.reaction.getKineticLaw() !== undefined) {
-    self.addListener("onShow", function () {
-      return MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
-    });
-  }
-}
-
-ReactionInfoWindow.prototype = Object.create(AbstractInfoWindow.prototype);
-ReactionInfoWindow.prototype.constructor = ReactionInfoWindow;
-
-/**
- * Methods that creates and return html code with the content of the window.
- *
- * @returns {Promise} representing html code for content of the info window
- */
-ReactionInfoWindow.prototype.createContentDiv = function () {
-  var self = this;
-  var reaction = self.getReactionData();
-  var result = document.createElement("div");
-  var title = document.createElement("h3");
-  title.innerHTML = "Reaction: " + reaction.getReactionId();
-  result.appendChild(title);
-
-  return self.createKineticsDiv(reaction, result).then(function () {
-    result.appendChild(self.createElementsDiv(reaction));
-    return result;
-  });
-};
-
-/**
- *
- * @param {Reaction} reaction
- * @param {HTMLElement} result
- * @returns {Promise}
- */
-ReactionInfoWindow.prototype.createKineticsDiv = function (reaction, result) {
-  var self = this;
-  if (reaction.getKineticLaw() === undefined) {
-    return Promise.resolve();
-  } else {
-    var kineticLaw = reaction.getKineticLaw();
-    return Functions.loadScript('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML').then(function () {
-      return MathJax.Hub.Config({
-        tex2jax: {
-          skipTags: ["script", "noscript", "style", "textarea", "pre", "code"]
-        }
-      });
-    }).then(function () {
-
-      result.appendChild(Functions.createElement({type: "h4", content: "Kinetic law"}));
-
-      if (kineticLaw.getMathMlPresentation() !== undefined) {
-        result.appendChild(Functions.createElement({
-          type: "div",
-          content: kineticLaw.getMathMlPresentation(),
-          xss: false
-        }));
-      } else {
-
-        result.appendChild(Functions.createElement({
-          type: "div",
-          content: "<p>Problematic MathML</p>"
-        }));
-        logger.warn("Problematic MathML: " + kineticLaw.getDefinition());
-      }
-      var promises = [];
-      for (var i = 0; i < kineticLaw.getFunctionIds().length; i++) {
-        promises.push(self.getCustomMap().getModel().getSbmlFunctionById(kineticLaw.getFunctionIds()[i]));
-      }
-      return Promise.all(promises);
-    }).then(function (functions) {
-      result.appendChild(self.createSbmlFunctionDiv(functions));
-
-      var promises = [];
-      for (var i = 0; i < kineticLaw.getParameterIds().length; i++) {
-        promises.push(self.getCustomMap().getModel().getSbmlParameterById(kineticLaw.getParameterIds()[i]));
-      }
-      return Promise.all(promises);
-    }).then(function (parameters) {
-      result.appendChild(self.createSbmlParameterDiv(parameters));
-    });
-  }
-};
-
-/**
- *
- * @param {SbmlFunction[]} functions
- * @returns {HTMLElement}
- */
-ReactionInfoWindow.prototype.createSbmlFunctionDiv = function (functions) {
-  var result = Functions.createElement({type: "div"});
-  if (functions.length > 0) {
-    result.appendChild(Functions.createElement({type: "h5", content: "Functions: "}));
-    var guiUtils = new GuiUtils();
-    var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
-    table.appendChild(guiUtils.createTableRow(["Name", "Definition", "Arguments"]));
-    for (var i = 0; i < functions.length; i++) {
-      var sbmlFunction = functions[i];
-      var mathML;
-      if (sbmlFunction.getMathMlPresentation() !== undefined) {
-        mathML = sbmlFunction.getMathMlPresentation();
-      } else {
-        mathML = "<p>Problematic MathML</p>";
-        logger.warn("Problematic MathML: " + sbmlFunction.getDefinition());
-      }
-      var functionArguments = sbmlFunction.getArguments().join(", ");
-      table.appendChild(guiUtils.createTableRow([sbmlFunction.getName(), mathML, functionArguments]));
-    }
-    result.appendChild(table);
-  }
-  return result;
-};
-
-/**
- *
- * @param {SbmlParameter[]} parameters
- * @returns {HTMLElement}
- */
-ReactionInfoWindow.prototype.createSbmlParameterDiv = function (parameters) {
-  var result = Functions.createElement({type: "div"});
-  if (parameters.length > 0) {
-    result.appendChild(Functions.createElement({type: "h5", content: "Parameters: "}));
-    var guiUtils = new GuiUtils();
-    var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
-    table.appendChild(guiUtils.createTableRow(["Name", "parameterId", "Value", "Global"]));
-    for (var i = 0; i < parameters.length; i++) {
-      var sbmlFunction = parameters[i];
-      table.appendChild(guiUtils.createTableRow([sbmlFunction.getName(), sbmlFunction.getParameterId(), sbmlFunction.getValue(), sbmlFunction.getGlobal()]));
-    }
-    result.appendChild(table);
-  }
-  return result;
-};
-
-/**
- *
- * @param {Reaction} reaction
- * @returns {HTMLElement}
- */
-ReactionInfoWindow.prototype.createElementsDiv = function (reaction) {
-  var result = Functions.createElement({type: "div"});
-  result.appendChild(Functions.createElement({type: "h5", content: "Elements: "}));
-  var guiUtils = new GuiUtils();
-  var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
-  table.appendChild(guiUtils.createTableRow(["Name", "Role", "elementId", "Constant", "Boundary condition", "Initial concentration", "Initial amount", "Stoichiometry"]));
-  var elements = reaction.getReactants();
-  var i, element;
-
-  function createRow(node, title) {
-    var element = node.getAlias();
-    var stoichiometry = node.getStoichiometry();
-    if (stoichiometry === undefined) {
-      stoichiometry = "";
-    }
-    return guiUtils.createTableRow([element.getName(), title, element.getElementId(), element.getConstant(), element.getBoundaryCondition(), element.getInitialConcentration(), element.getInitialAmount(), stoichiometry])
-  }
-
-  for (i = 0; i < elements.length; i++) {
-    element = elements[i].getAlias();
-    table.appendChild(createRow(elements[i], "Reactant"));
-  }
-  elements = reaction.getProducts();
-  for (i = 0; i < elements.length; i++) {
-    element = elements[i].getAlias();
-    table.appendChild(createRow(elements[i], "Product"));
-  }
-  elements = reaction.getModifiers();
-  for (i = 0; i < elements.length; i++) {
-    element = elements[i].getAlias();
-    table.appendChild(createRow(elements[i], "Modifier"));
-  }
-  result.appendChild(table);
-  return result;
-};
-
-/**
- *
- * @returns {PromiseLike<any> | Promise<any>}
- */
-ReactionInfoWindow.prototype.init = function () {
-  var self = this;
-  return Promise.resolve().then(function () {
-    return AbstractInfoWindow.prototype.init.call(self);
-  }).then(function () {
-    return self.update();
-  });
-};
-
-/**
- * Returns array with data taken from all known {@link AbstractDbOverlay}.
- *
- * @param {Object.<string,boolean>} general
- * @returns {Promise} of an array with data from {@link AbstractDbOverlay}
- */
-ReactionInfoWindow.prototype.getOverlaysData = function (general) {
-  return this.getCustomMap().getTopMap().getOverlayDataForReaction(this.getReactionData(), general);
-};
-
-/**
- *
- * @returns {Reaction}
- */
-ReactionInfoWindow.prototype.getReactionData = function () {
-  return this._reactionData;
-};
-
-/**
- *
- * @param {Reaction} reactionData
- */
-ReactionInfoWindow.prototype.setReactionData = function (reactionData) {
-  if (reactionData === undefined || reactionData === null) {
-    throw new Error("Reaction must be specified");
-  } else if (reactionData instanceof Reaction) {
-    this._reactionData = reactionData;
-  } else {
-    throw new Error("Parameter must be of Reaction type, but found" + reactionData);
-  }
-};
-
-/**
- *
- * @returns {Point}
- */
-ReactionInfoWindow.prototype.getPosition = function () {
-  return this.getReactionData().getCenter();
-};
-
-module.exports = ReactionInfoWindow;
+"use strict";
+
+var Promise = require("bluebird");
+
+var AbstractInfoWindow = require('./AbstractInfoWindow');
+var GuiUtils = require('../../gui/leftPanel/GuiUtils');
+var IdentifiedElement = require('../data/IdentifiedElement');
+var Reaction = require('../data/Reaction');
+var Functions = require('../../Functions');
+var logger = require('../../logger');
+
+/**
+ * Class representing info window that should be opened when clicking on
+ * reaction.
+ */
+/**
+ *
+ * @param {IdentifiedElement} [params.identifiedElement]
+ * @param {AbstractCustomMap} params.map
+ * @param {Marker} params.marker
+ * @param {Reaction} params.reaction
+ *
+ * @constructor
+ * @extends AbstractInfoWindow
+ */
+function ReactionInfoWindow(params) {
+  if (params.identifiedElement === undefined) {
+    params.identifiedElement = new IdentifiedElement(params.reaction);
+  }
+  // call super constructor
+  AbstractInfoWindow.call(this, params);
+
+  var self = this;
+
+  self.setReactionData(params.reaction);
+  if (params.reaction.getKineticLaw() !== undefined) {
+    self.addListener("onShow", function () {
+      return MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
+    });
+  }
+}
+
+ReactionInfoWindow.prototype = Object.create(AbstractInfoWindow.prototype);
+ReactionInfoWindow.prototype.constructor = ReactionInfoWindow;
+
+/**
+ * Methods that creates and return html code with the content of the window.
+ *
+ * @returns {Promise} representing html code for content of the info window
+ */
+ReactionInfoWindow.prototype.createContentDiv = function () {
+  var self = this;
+  var reaction = self.getReactionData();
+  var result = document.createElement("div");
+  var title = document.createElement("h3");
+  title.innerHTML = "Reaction: " + reaction.getReactionId();
+  result.appendChild(title);
+
+  return self.createKineticsDiv(reaction, result).then(function () {
+    result.appendChild(self.createElementsDiv(reaction));
+    return result;
+  });
+};
+
+/**
+ *
+ * @param {Reaction} reaction
+ * @param {HTMLElement} result
+ * @returns {Promise}
+ */
+ReactionInfoWindow.prototype.createKineticsDiv = function (reaction, result) {
+  var self = this;
+  if (reaction.getKineticLaw() === undefined) {
+    return Promise.resolve();
+  } else {
+    var kineticLaw = reaction.getKineticLaw();
+    return Functions.loadScript('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML').then(function () {
+      return MathJax.Hub.Config({
+        tex2jax: {
+          skipTags: ["script", "noscript", "style", "textarea", "pre", "code"]
+        }
+      });
+    }).then(function () {
+
+      result.appendChild(Functions.createElement({type: "h4", content: "Kinetic law"}));
+
+      if (kineticLaw.getMathMlPresentation() !== undefined) {
+        result.appendChild(Functions.createElement({
+          type: "div",
+          content: kineticLaw.getMathMlPresentation(),
+          xss: false
+        }));
+      } else {
+
+        result.appendChild(Functions.createElement({
+          type: "div",
+          content: "<p>Problematic MathML</p>"
+        }));
+        logger.warn("Problematic MathML: " + kineticLaw.getDefinition());
+      }
+      var promises = [];
+      for (var i = 0; i < kineticLaw.getFunctionIds().length; i++) {
+        promises.push(self.getCustomMap().getModel().getSbmlFunctionById(kineticLaw.getFunctionIds()[i]));
+      }
+      return Promise.all(promises);
+    }).then(function (functions) {
+      result.appendChild(self.createSbmlFunctionDiv(functions));
+
+      var promises = [];
+      for (var i = 0; i < kineticLaw.getParameterIds().length; i++) {
+        promises.push(self.getCustomMap().getModel().getSbmlParameterById(kineticLaw.getParameterIds()[i]));
+      }
+      return Promise.all(promises);
+    }).then(function (parameters) {
+      result.appendChild(self.createSbmlParameterDiv(parameters));
+    });
+  }
+};
+
+/**
+ *
+ * @param {SbmlFunction[]} functions
+ * @returns {HTMLElement}
+ */
+ReactionInfoWindow.prototype.createSbmlFunctionDiv = function (functions) {
+  var result = Functions.createElement({type: "div"});
+  if (functions.length > 0) {
+    result.appendChild(Functions.createElement({type: "h5", content: "Functions: "}));
+    var guiUtils = new GuiUtils();
+    var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
+    table.appendChild(guiUtils.createTableRow(["Name", "Definition", "Arguments"]));
+    for (var i = 0; i < functions.length; i++) {
+      var sbmlFunction = functions[i];
+      var mathML;
+      if (sbmlFunction.getMathMlPresentation() !== undefined) {
+        mathML = sbmlFunction.getMathMlPresentation();
+      } else {
+        mathML = "<p>Problematic MathML</p>";
+        logger.warn("Problematic MathML: " + sbmlFunction.getDefinition());
+      }
+      var functionArguments = sbmlFunction.getArguments().join(", ");
+      table.appendChild(guiUtils.createTableRow([sbmlFunction.getName(), mathML, functionArguments]));
+    }
+    result.appendChild(table);
+  }
+  return result;
+};
+
+/**
+ *
+ * @param {SbmlParameter[]} parameters
+ * @returns {HTMLElement}
+ */
+ReactionInfoWindow.prototype.createSbmlParameterDiv = function (parameters) {
+  var result = Functions.createElement({type: "div"});
+  if (parameters.length > 0) {
+    result.appendChild(Functions.createElement({type: "h5", content: "Parameters: "}));
+    var guiUtils = new GuiUtils();
+    var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
+    table.appendChild(guiUtils.createTableRow(["Name", "parameterId", "Value", "Global"]));
+    for (var i = 0; i < parameters.length; i++) {
+      var sbmlFunction = parameters[i];
+      table.appendChild(guiUtils.createTableRow([sbmlFunction.getName(), sbmlFunction.getParameterId(), sbmlFunction.getValue(), sbmlFunction.getGlobal()]));
+    }
+    result.appendChild(table);
+  }
+  return result;
+};
+
+/**
+ *
+ * @param {Reaction} reaction
+ * @returns {HTMLElement}
+ */
+ReactionInfoWindow.prototype.createElementsDiv = function (reaction) {
+  var result = Functions.createElement({type: "div"});
+  result.appendChild(Functions.createElement({type: "h5", content: "Elements: "}));
+  var guiUtils = new GuiUtils();
+  var table = Functions.createElement({type: "div", style: "display: table;", className: "borderTable"});
+  table.appendChild(guiUtils.createTableRow(["Name", "Role", "elementId", "Constant", "Boundary condition", "Initial concentration", "Initial amount", "Stoichiometry"]));
+  var elements = reaction.getReactants();
+  var i, element;
+
+  function createRow(node, title) {
+    var element = node.getAlias();
+    var stoichiometry = node.getStoichiometry();
+    if (stoichiometry === undefined) {
+      stoichiometry = "";
+    }
+    return guiUtils.createTableRow([element.getName(), title, element.getElementId(), element.getConstant(), element.getBoundaryCondition(), element.getInitialConcentration(), element.getInitialAmount(), stoichiometry])
+  }
+
+  for (i = 0; i < elements.length; i++) {
+    element = elements[i].getAlias();
+    table.appendChild(createRow(elements[i], "Reactant"));
+  }
+  elements = reaction.getProducts();
+  for (i = 0; i < elements.length; i++) {
+    element = elements[i].getAlias();
+    table.appendChild(createRow(elements[i], "Product"));
+  }
+  elements = reaction.getModifiers();
+  for (i = 0; i < elements.length; i++) {
+    element = elements[i].getAlias();
+    table.appendChild(createRow(elements[i], "Modifier"));
+  }
+  result.appendChild(table);
+  return result;
+};
+
+/**
+ *
+ * @returns {PromiseLike<any> | Promise<any>}
+ */
+ReactionInfoWindow.prototype.init = function () {
+  var self = this;
+  return Promise.resolve().then(function () {
+    return AbstractInfoWindow.prototype.init.call(self);
+  }).then(function () {
+    return self.update();
+  });
+};
+
+/**
+ * Returns array with data taken from all known {@link AbstractDbOverlay}.
+ *
+ * @param {Object.<string,boolean>} general
+ * @returns {Promise} of an array with data from {@link AbstractDbOverlay}
+ */
+ReactionInfoWindow.prototype.getDbOverlaysData = function (general) {
+  return this.getCustomMap().getTopMap().getOverlayDataForReaction(this.getReactionData(), general);
+};
+
+/**
+ *
+ * @returns {Reaction}
+ */
+ReactionInfoWindow.prototype.getReactionData = function () {
+  return this._reactionData;
+};
+
+/**
+ *
+ * @param {Reaction} reactionData
+ */
+ReactionInfoWindow.prototype.setReactionData = function (reactionData) {
+  if (reactionData === undefined || reactionData === null) {
+    throw new Error("Reaction must be specified");
+  } else if (reactionData instanceof Reaction) {
+    this._reactionData = reactionData;
+  } else {
+    throw new Error("Parameter must be of Reaction type, but found" + reactionData);
+  }
+};
+
+/**
+ *
+ * @returns {Point}
+ */
+ReactionInfoWindow.prototype.getPosition = function () {
+  return this.getReactionData().getCenter();
+};
+
+module.exports = ReactionInfoWindow;
diff --git a/frontend-js/src/test/js/helper.js b/frontend-js/src/test/js/helper.js
index 30002e661c6ffe0c06d0535660fc507cbfbc276a..11fc0efa1884a43783a7801b84373de106a9b633 100644
--- a/frontend-js/src/test/js/helper.js
+++ b/frontend-js/src/test/js/helper.js
@@ -223,9 +223,11 @@ Helper.prototype.createAlias = function (map) {
 /**
  *
  * @param {Alias} [alias]
+ * @param {string} [type]
+ *
  * @returns {LayoutAlias}
  */
-Helper.prototype.createLayoutAlias = function (alias) {
+Helper.prototype.createLayoutAlias = function (alias, type) {
   var id;
   var modelId;
   if (alias instanceof Alias) {
@@ -242,7 +244,8 @@ Helper.prototype.createLayoutAlias = function (alias) {
       a: 23
     },
     modelId: modelId,
-    geneVariations: []
+    geneVariations: [],
+    type: type
   });
 };
 
diff --git a/frontend-js/src/test/js/map/CustomMap-test.js b/frontend-js/src/test/js/map/CustomMap-test.js
index 9c8cf3654a232c44fa8853d325fd2631a9d73fda..880efabfaac797fc04af47722fd81ac9caf3c0b3 100644
--- a/frontend-js/src/test/js/map/CustomMap-test.js
+++ b/frontend-js/src/test/js/map/CustomMap-test.js
@@ -1,1037 +1,1021 @@
-"use strict";
-
-var Promise = require("bluebird");
-
-require("../mocha-config.js");
-
-var AliasMarker = require('../../../main/js/map/marker/AliasMarker');
-var AliasSurface = require('../../../main/js/map/surface/AliasSurface');
-var Comment = require('../../../main/js/map/data/Comment');
-var ControlType = require('../../../main/js/map/ControlType');
-var CustomMap = require('../../../main/js/map/CustomMap');
-var IdentifiedElement = require('../../../main/js/map/data/IdentifiedElement');
-var MapContextMenu = require('../../../main/js/gui/MapContextMenu');
-var MolArt = require('../../../main/js/map/structure/MolArt');
-var Point = require('../../../main/js/map/canvas/Point');
-var PointData = require('../../../main/js/map/data/PointData');
-var PointMarker = require('../../../main/js/map/marker/PointMarker');
-var ReactionMarker = require('../../../main/js/map/marker/ReactionMarker');
-var ReactionSurface = require('../../../main/js/map/surface/ReactionSurface');
-
-var ServerConnector = require('./../ServerConnector-mock');
-
-var logger = require('./../logger');
-
-var chai = require('chai');
-var assert = chai.assert;
-
-describe('CustomMap', function () {
-  describe("constructor", function () {
-    it("default", function () {
-      var options = helper.createCustomMapOptions();
-      var map = new CustomMap(options);
-      assert.ok(map);
-    });
-
-    it("with submaps", function () {
-      var options = helper.createCustomMapOptions();
-
-      options.getProject().addModel(helper.createModel());
-
-      var map = new CustomMap(options);
-      assert.ok(map);
-    });
-
-    it("with session data pointing to not existing overlay", function () {
-      var options = helper.createCustomMapOptions();
-
-      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay("-1");
-
-      var map = new CustomMap(options);
-      assert.ok(map);
-    });
-  });
-
-  it("getSubmapById", function () {
-    var map = helper.createCustomMap();
-    assert.ok(map.getSubmapById(map.getId()));
-  });
-
-  it("getSubmapById (invalid)", function () {
-    var map = helper.createCustomMap();
-    assert.equal(map.getSubmapById(-1), null);
-  });
-
-  it("getSubmapById (string id)", function () {
-    var map = helper.createCustomMap();
-    assert.ok(map.getSubmapById(map.getId() + ""));
-  });
-
-  describe("openDataOverlay", function () {
-    it("for not existing id", function () {
-      var map = helper.createCustomMap();
-      try {
-        map.openDataOverlay(-1);
-        assert.ok(false);
-      } catch (exception) {
-        assert.ok(exception.message.indexOf("You have no privileges") >= 0);
-      }
-    });
-
-    it("for int id", function () {
-      var options = helper.createCustomMapOptions();
-      var layout = options.getProject().getDataOverlays()[0];
-      var map = new CustomMap(options);
-      map.openDataOverlay(layout.getId());
-      assert.equal(logger.getErrors().length, 0);
-    });
-
-    it("for overlay object", function () {
-      var options = helper.createCustomMapOptions();
-      var layout = options.getProject().getDataOverlays()[0];
-      var map = new CustomMap(options);
-      map.openDataOverlay(layout);
-      assert.equal(logger.getErrors().length, 0);
-    });
-
-    it("for string id", function () {
-      var options = helper.createCustomMapOptions();
-      var layout = options.getProject().getDataOverlays()[0];
-      var map = new CustomMap(options);
-      map.openDataOverlay(layout.getId() + "");
-      assert.equal(logger.getErrors().length, 0);
-    });
-
-    it("with background overlay", function () {
-      var map = helper.createCustomMap();
-      var layout = helper.createOverlay();
-      layout.setInitialized(true);
-      layout.setInputDataAvailable(false);
-      map.getProject().addDataOverlay(layout);
-
-      return map.openDataOverlay(layout.getId()).then(function () {
-        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
-        assert.equal(0, vLayouts.length);
-      });
-    });
-
-    it("with non background overlay", function () {
-      var project = helper.createProject();
-      var layout = helper.createOverlay();
-      layout.setInitialized(true);
-      layout.setInputDataAvailable(true);
-      project.addDataOverlay(layout);
-
-      var emptyBackground = helper.createOverlay();
-      emptyBackground.setInputDataAvailable(false);
-      emptyBackground.setName("Empty");
-      project.addDataOverlay(emptyBackground);
-
-      var map = helper.createCustomMap(project);
-
-
-      return map.openDataOverlay(layout.getId()).then(function () {
-        var backgroundId = ServerConnector.getSessionData(project).getSelectedBackgroundOverlay();
-        assert.equal(backgroundId, emptyBackground.getId());
-      });
-    });
-
-    it("check backgroundChangeListener", function () {
-      var project = helper.createProject();
-      var overlay = helper.createOverlay();
-      overlay.setInitialized(true);
-      overlay.setInputDataAvailable(true);
-      project.addDataOverlay(overlay);
-
-      var overlay2 = helper.createOverlay();
-      overlay2.setInitialized(true);
-      overlay2.setInputDataAvailable(true);
-      project.addDataOverlay(overlay2);
-
-      var emptyBackground = helper.createOverlay();
-      emptyBackground.setInputDataAvailable(false);
-      emptyBackground.setName("Empty");
-      project.addDataOverlay(emptyBackground);
-
-      var map = helper.createCustomMap(project);
-
-      var counter = 0;
-      map.addListener("onBackgroundOverlayChange", function () {
-        counter++;
-      });
-
-      return map.openDataOverlay(overlay.getId()).then(function () {
-        assert.equal(1, counter, "listener wasn't fired");
-        return map.openDataOverlay(overlay2.getId());
-      }).then(function () {
-        assert.equal(1, counter, "listener shouldn't be fired again because nothing changed");
-      });
-    });
-
-    it("simple", function () {
-      var map = helper.createCustomMap();
-      var alias = helper.createAlias(map);
-      map.getModel().addAlias(alias);
-
-      var reaction = helper.createReaction(map);
-      map.getModel().addReaction(reaction);
-
-      var layout = helper.createOverlay();
-      layout.setInputDataAvailable(true);
-      layout.setInitialized(true);
-      var layoutAlias = helper.createLayoutAlias(alias);
-      layout.addAlias(layoutAlias);
-
-      var layoutReaction = helper.createLayoutReaction(reaction);
-      layout.addReaction(layoutReaction);
-
-      map.getProject().addDataOverlay(layout);
-
-      return map.openDataOverlay(layout.getId()).then(function () {
-        return map._showSelectedDataOverlay(layout.getId(), 0, 1);
-      }).then(function () {
-        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
-        assert.equal(1, vLayouts.length);
-        assert.equal(layout.getId(), vLayouts[0]);
-
-        assert.equal(2, map.selectedLayoutOverlays[layout.getId()].length);
-        assert.ok(map.selectedLayoutOverlays[layout.getId()][0] instanceof AliasSurface);
-        assert.ok(map.selectedLayoutOverlays[layout.getId()][1] instanceof ReactionSurface);
-
-        // now hide the layout
-        return map._hideSelectedLayout(layout.getId());
-      }).then(function () {
-        assert.ok(map.selectedLayoutOverlays[layout.getId()]);
-        assert.equal(0, map.selectedLayoutOverlays[layout.getId()].length);
-      });
-
-    });
-
-    it("with submaps", function () {
-      var projectId = "complex_model_with_submaps";
-      helper.setUrl("http://test/?id=" + projectId);
-      var customMap;
-      var emptySubmodelId = 16730;
-      var filledSubmodelId = 16731;
-      var overlayId = 18083;
-
-      return ServerConnector.getProject(projectId).then(function (project) {
-        var options = helper.createCustomMapOptions(project);
-        customMap = new CustomMap(options);
-
-        return customMap.openSubmap(emptySubmodelId);
-      }).then(function () {
-        return customMap.openSubmap(filledSubmodelId);
-      }).then(function () {
-
-        var emptySubmap = customMap.getSubmapById(emptySubmodelId);
-        var filledSubmap = customMap.getSubmapById(filledSubmodelId);
-
-        return customMap.openDataOverlay(overlayId).then(function () {
-
-          assert.equal(1, customMap.selectedLayoutOverlays[overlayId].length);
-          assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
-          assert.equal(0, emptySubmap.selectedLayoutOverlays[overlayId].length);
-
-          // now hide the layout
-
-          return customMap.hideSelectedLayout(overlayId);
-        }).then(function () {
-          assert.equal(0, customMap.selectedLayoutOverlays[overlayId].length);
-          assert.equal(0, filledSubmap.selectedLayoutOverlays[overlayId].length);
-          assert.equal(0, emptySubmap.selectedLayoutOverlays[overlayId].length);
-        });
-
-      }).then(function () {
-        return customMap.destroy();
-      });
-    });
-
-    it("with submap double opened", function () {
-      var projectId = "complex_model_with_submaps";
-      helper.setUrl("http://test/?id=" + projectId);
-      var customMap, filledSubmap;
-      var filledSubmodelId = 16731;
-      var overlayId = 18083;
-
-      return ServerConnector.getProject(projectId).then(function (project) {
-        customMap = helper.createCustomMap(project);
-        return customMap.openSubmap(filledSubmodelId);
-      }).then(function () {
-        filledSubmap = customMap.getSubmapById(filledSubmodelId);
-        return customMap.openDataOverlay(overlayId);
-      }).then(function () {
-        assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
-        return customMap.openSubmap(filledSubmodelId);
-      }).then(function () {
-        //after opening submap nothing should change
-        assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
-      }).finally(function () {
-        return customMap.destroy();
-      });
-    });
-
-    it("check if info window content changed", function () {
-      var project = helper.createProject();
-      var layout = helper.createOverlay();
-      layout.setInitialized(true);
-      layout.setInputDataAvailable(true);
-      project.addDataOverlay(layout);
-
-      var map = helper.createCustomMap(project);
-
-      var alias = helper.createAlias(map);
-      var ie = helper.createIdentifiedElement(alias);
-      var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
-      var infoWindow;
-      return map.openInfoWindowForIdentifiedElement(ie, marker).then(function (result) {
-        infoWindow = result;
-        assert.notOk(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
-        return map.openDataOverlay(layout.getId());
-      }).then(function () {
-        assert.ok(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
-        assert.equal(logger.getErrors().length, 0);
-      });
-    });
-  });
-
-  describe("hideDataOverlay", function () {
-    it("default", function () {
-      var map = helper.createCustomMap();
-      var layout = helper.createOverlay();
-      layout.setInitialized(true);
-      layout.setInputDataAvailable(true);
-      map.getProject().addDataOverlay(layout);
-
-      return map.openDataOverlay(layout.getId()).then(function () {
-        return map.hideDataOverlay(layout.getId());
-      }).then(function () {
-        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
-        assert.equal(0, vLayouts.length);
-      });
-    });
-    it("check if info window content changed", function () {
-      var project = helper.createProject();
-      var layout = helper.createOverlay();
-      layout.setInitialized(true);
-      layout.setInputDataAvailable(true);
-      project.addDataOverlay(layout);
-
-      var map = helper.createCustomMap(project);
-
-      var alias = helper.createAlias(map);
-      var ie = helper.createIdentifiedElement(alias);
-      var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
-      var infoWindow;
-      return map.openInfoWindowForIdentifiedElement(ie, marker).then(function (result) {
-        infoWindow = result;
-        return map.openDataOverlay(layout.getId());
-      }).then(function () {
-        assert.ok(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
-        return map.hideDataOverlay(layout.getId());
-      }).then(function () {
-        assert.notOk(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
-        assert.equal(logger.getErrors().length, 0);
-      });
-    });
-  });
-
-
-  describe("renderOverlayCollection", function () {
-    it("for alias", function () {
-      var map = helper.createCustomMap();
-      var alias = helper.createAlias(map);
-
-      var oc = helper.createDbOverlay(map);
-
-      oc.getIdentifiedElements = function () {
-        return Promise.resolve([new IdentifiedElement({
-          objectId: alias.getId(),
-          icon: "empty.png",
-          modelId: map.getId(),
-          type: "Alias"
-        })]);
-      };
-      return map.renderOverlayCollection({
-        overlayCollection: oc
-      }).then(function () {
-        var markers = map.getMarkerSurfaceCollection().getMarkers();
-        assert.equal(1, markers.length);
-      });
-    });
-
-    it("alias re-rendering with different icon", function () {
-      var map = helper.createCustomMap();
-      var reaction = helper.createReaction(map, true);
-      var alias = helper.createAlias(map);
-      map.getModel().addAlias(alias);
-      map.getModel().addReaction(reaction);
-
-      var identifiedElement = new IdentifiedElement(alias);
-      identifiedElement.setIcon("empty.png");
-      var identifiedElement2 = new IdentifiedElement(alias);
-      identifiedElement2.setIcon("new_icon.png");
-
-      var marker;
-      var oc = helper.createDbOverlay(map);
-
-      oc.getIdentifiedElements = function () {
-        return Promise.resolve([identifiedElement, new IdentifiedElement(reaction)]);
-      };
-      return map.renderOverlayCollection({
-        overlayCollection: oc
-      }).then(function () {
-        marker = map.getMarkerSurfaceCollection().getMarker(identifiedElement);
-        oc.getIdentifiedElements = function () {
-          return Promise.resolve([identifiedElement2]);
-        };
-        assert.ok(map.getMarkerSurfaceCollection().getSurface({element: reaction, overlay: oc}));
-
-        return map.renderOverlayCollection({
-          overlayCollection: oc
-        });
-      }).then(function () {
-        assert.equal(marker.getIcon(), "new_icon.png");
-
-        assert.notOk(oc.mapOverlays["REACTION"][reaction.getId()]);
-      });
-    });
-
-    it("for set of aliases", function () {
-      var map = helper.createCustomMap();
-
-      var oc = helper.createDbOverlay(map);
-
-      var identifiedElements = [];
-      for (var i = 0; i < 3; i++) {
-        var alias = helper.createAlias(map);
-        map.getModel().addAlias(alias);
-        var ie = helper.createIdentifiedElement(alias);
-        ie.setIcon("");
-        identifiedElements.push(ie);
-      }
-
-      oc.getIdentifiedElements = function () {
-        return Promise.resolve(identifiedElements);
-      };
-
-      return map.renderOverlayCollection({
-        overlayCollection: oc
-      }).then(function () {
-        var markers = map.getMarkerSurfaceCollection().getMarkers();
-        assert.equal(3, markers.length);
-      });
-    });
-
-    it("for point", function () {
-      var map = helper.createCustomMap();
-
-      var oc = helper.createDbOverlay(map);
-
-      var javaObj = {
-        objectId: "Point2D.Double[117.685546875, 204.6923828125001]",
-        modelId: map.getId(),
-        type: "POINT",
-        icon: "marker/empty.png"
-      };
-
-      oc.getIdentifiedElements = function () {
-        return Promise.resolve([new IdentifiedElement(javaObj)]);
-      };
-
-      return map.renderOverlayCollection({
-        overlayCollection: oc
-      }).then(function () {
-        var markers = map.getMarkerSurfaceCollection().getMarkers();
-        assert.equal(1, markers.length);
-      });
-    });
-
-    it("for reaction", function () {
-      var map = helper.createCustomMap();
-      var reaction = helper.createReaction(map, true);
-      map.getModel().addReaction(reaction);
-
-      var oc = helper.createDbOverlay(map);
-
-      oc.getIdentifiedElements = function () {
-        return Promise.resolve([new IdentifiedElement(reaction)]);
-      };
-
-      return map.renderOverlayCollection({
-        overlayCollection: oc
-      }).then(function () {
-        var surfaces = map.getMarkerSurfaceCollection().getSurfaces();
-        assert.equal(1, surfaces.length);
-      });
-    });
-  });
-
-  it("refreshMarkers", function () {
-    var map = helper.createCustomMap();
-    var alias = helper.createAlias(map);
-    map.getModel().addAlias(alias);
-
-    var oc = helper.createDbOverlay(map);
-
-    oc.getIdentifiedElements = function () {
-      var element = new IdentifiedElement(alias);
-      element.setIcon("icon");
-      return Promise.resolve([element]);
-    };
-    return map.renderOverlayCollection({
-      overlayCollection: oc
-    }).then(function () {
-      return map.refreshMarkers(true);
-    }).then(function () {
-      var markers = map.getMarkerSurfaceCollection().getMarkers();
-      assert.equal(1, markers.length);
-    });
-  });
-
-  it("clearDbOverlays", function () {
-    var map = helper.createCustomMap();
-
-    var oc = helper.createDbOverlay(map);
-
-    var javaObj = {
-      objectId: "Point2D.Double[117.685546875, 204.6923828125001]",
-      modelId: map.getId(),
-      type: "POINT",
-      icon: "marker/empty.png"
-    };
-    var searchResults = [new IdentifiedElement(javaObj)];
-
-    oc.getIdentifiedElements = function () {
-      return Promise.resolve(searchResults);
-    };
-    oc.clear = function () {
-      searchResults = [];
-      return this.callListeners("onSearch", searchResults);
-    };
-
-    return map.renderOverlayCollection({
-      overlayCollection: oc
-    }).then(function () {
-
-      return map.clearDbOverlays();
-    }).then(function () {
-      var markerCount = 0;
-      for (var id in oc.pointMarkers) {
-        if (oc.pointMarkers.hasOwnProperty(id)) {
-          markerCount++;
-        }
-      }
-
-      assert.equal(0, markerCount);
-    });
-
-  });
-
-  it("getInfoWindowForIdentifiedElement ( reaction)", function () {
-    var map = helper.createCustomMap();
-    var reaction = helper.createReaction(map, true);
-    map.getModel().addReaction(reaction);
-
-    var ie = helper.createIdentifiedElement(reaction);
-    var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
-
-    var infoWindow = map.getInfoWindowForIdentifiedElement(ie);
-
-    assert.equal(null, infoWindow);
-    return map.openInfoWindowForIdentifiedElement(ie, marker).then(function () {
-      infoWindow = map.getInfoWindowForIdentifiedElement(ie);
-
-      assert.ok(infoWindow);
-    });
-
-  });
-
-  it("right click on map", function () {
-    var map = helper.createCustomMap();
-    map.setContextMenu(new MapContextMenu({
-      customMap: map,
-      element: testDiv,
-      molArt: new MolArt(map.getElement(), map)
-    }));
-
-    return map.getContextMenu().init().then(function () {
-      var data = {
-        stop: null,
-        point: new Point(10.0, 20.0)
-      };
-
-      assert.notOk(map.getActiveSubmapId());
-      return map.triggerEvent("map-rightclick", data);
-    }).then(function () {
-      //we need to wait because some implementations don't use promises...
-      return Promise.delay(100);
-    }).then(function () {
-      assert.equal(map.getId(), map.getActiveSubmapId());
-      return map.destroy();
-    });
-
-  });
-
-  it("left click on map", function () {
-    var map;
-    var searchOverlay;
-    return ServerConnector.getProject().then(function (project) {
-      map = helper.createCustomMap(project);
-      searchOverlay = helper.createSearchDbOverlay(map);
-
-      var data = {
-        stop: null,
-        point: new Point(184.79, 365.76)
-      };
-      map.setZoom(4);
-
-      assert.notOk(map.getActiveSubmapId());
-      return map.triggerEvent("map-click", data);
-    }).then(function () {
-      //we need to wait because some implementations don't use promises...
-      return Promise.delay(100);
-    }).then(function () {
-      assert.equal(map.getId(), map.getActiveSubmapId());
-      var element = new IdentifiedElement({id: 329171, type: "ALIAS", modelId: map.getId()});
-      assert.ok(map.getMarkerSurfaceCollection().getMarker(element));
-    });
-
-  });
-
-  it("left click on reaction", function () {
-    var map;
-    var searchOverlay;
-    return ServerConnector.getProject().then(function (project) {
-      map = helper.createCustomMap(project);
-      searchOverlay = helper.createSearchDbOverlay(map);
-
-      var data = {
-        stop: null,
-        point: new Point(457.51, 356.84)
-      };
-      map.setZoom(4);
-
-      assert.notOk(map.getActiveSubmapId());
-      return map.triggerEvent("map-click", data);
-    }).then(function () {
-      //we need to wait because some implementations don't use promises...
-      return Promise.delay(100);
-    }).then(function () {
-      assert.equal(map.getId(), map.getActiveSubmapId());
-
-      var reaction = new IdentifiedElement({id: 153521, type: "REACTION", modelId: map.getId()});
-      var surface = map.getMarkerSurfaceCollection().getSurface({element: reaction, overlay: searchOverlay});
-
-      assert.ok(surface);
-      assert.ok(surface.isShown());
-
-      var element = new IdentifiedElement({id: 329165, type: "ALIAS", modelId: map.getId()});
-      var marker = map.getMarkerSurfaceCollection().getMarker(element);
-      assert.ok(marker);
-    });
-  });
-
-  it("getAliasVisibleLayoutsData", function () {
-    var mockObject = helper.createCustomMap();
-
-    var alias = helper.createAlias();
-    mockObject.getModel().addAlias(alias);
-
-    var layout = helper.createOverlay();
-    layout.setInputDataAvailable(true);
-    layout.setInitialized(true);
-    mockObject.getProject().addDataOverlay(layout);
-
-    return mockObject.openDataOverlay(layout.getId()).then(function () {
-      return mockObject.getAliasVisibleLayoutsData(alias.getId());
-    }).then(function (layoutAliases) {
-      assert.equal(layoutAliases.length, 1);
-    });
-  });
-
-  it("changed coordinates in map", function () {
-    var map = helper.createCustomMap();
-    var oldCenter = map.getCenter();
-    var newCenter = new Point(3, 87);
-    map.setCenter(newCenter);
-    map.triggerEvent("map-center_changed");
-
-    var center = ServerConnector.getSessionData(map.getProject()).getCenter(map.getModel());
-    assert.ok(center !== oldCenter);
-    assert.ok(center instanceof Point);
-  });
-
-  it("refreshComments", function () {
-    var map = helper.createCustomMap();
-    map.getModel().setId(15781);
-    helper.createCommentDbOverlay(map);
-
-    ServerConnector.getSessionData().setShowComments(true);
-    return map.refreshComments().then(function () {
-      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
-        id: '(241.01,372.35)',
-        modelId: map.getId(),
-        type: "POINT"
-      })));
-      assert.ok(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
-        id: '(643.96,144.09)',
-        modelId: map.getId(),
-        type: "POINT"
-      })));
-      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
-        id: '(216.65,370.00)',
-        modelId: map.getId(),
-        type: "POINT"
-      })));
-      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
-        id: 'unkId',
-        modelId: map.getId(),
-        type: "POINT"
-      })));
-    });
-  });
-
-  it("hide comments", function () {
-    var map = helper.createCustomMap();
-    map.getModel().setId(15781);
-    helper.createCommentDbOverlay(map);
-
-    ServerConnector.getSessionData().setShowComments(true);
-    return map.refreshComments().then(function () {
-      ServerConnector.getSessionData().setShowComments(false);
-      return map.refreshComments();
-    }).then(function () {
-      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
-        id: '(241.01, 372.35)',
-        modelId: map.getId(),
-        type: "POINT"
-      })));
-    });
-  });
-
-  it("openCommentDialog", function () {
-    var map = helper.createCustomMap();
-    map.getModel().setId(15781);
-    map.setActiveSubmapId(15781);
-    map.setActiveSubmapClickCoordinates(new Point(2, 12));
-    return map.openCommentDialog().then(function () {
-      var types = map.getCommentDialog().getTypes();
-      assert.equal(types.length, 6);
-      var selected = map.getCommentDialog().getSelectedType();
-      assert.ok(selected === "<General>");
-
-      map.getCommentDialog().setSelectedType(1);
-      selected = map.getCommentDialog().getSelectedType();
-      assert.notOk(selected === "<General>");
-
-      map.getCommentDialog().setSelectedType(2);
-      selected = map.getCommentDialog().getSelectedType();
-      assert.notOk(selected === "<General>");
-    }).then(function () {
-      map.getCommentDialog().destroy();
-    });
-  });
-
-  describe("addComment", function () {
-    it("default", function () {
-      var map;
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        helper.createCommentDbOverlay(map);
-        map.setActiveSubmapId(map.getId());
-        map.setActiveSubmapClickCoordinates(new Point(2, 12));
-        return map.openCommentDialog();
-      }).then(function () {
-        return map.getCommentDialog().addComment();
-      }).then(function (comments) {
-        assert.equal(0, comments.length);
-        map.getCommentDialog().destroy();
-      });
-    });
-
-    it("when comments are visible", function () {
-      var map;
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        helper.createCommentDbOverlay(map);
-        map.setActiveSubmapId(map.getId());
-        map.setActiveSubmapClickCoordinates(new Point(2, 12));
-        return map.openCommentDialog();
-      }).then(function () {
-        ServerConnector.getSessionData().setShowComments(true);
-        return map.getCommentDialog().addComment();
-      }).then(function (comments) {
-        assert.ok(comments.length > 0);
-        map.getCommentDialog().destroy();
-      });
-    });
-
-    it("add for protein", function () {
-      var map;
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        helper.createCommentDbOverlay(map);
-        map.setActiveSubmapId(map.getId());
-        map.setActiveSubmapClickCoordinates(new Point(2, 12));
-        return map.openCommentDialog();
-      }).then(function () {
-        var dialog = map.getCommentDialog();
-        dialog.setSelectedType(1);
-        return dialog.addComment();
-      }).then(function () {
-        map.getCommentDialog().destroy();
-      });
-    });
-  });
-
-  it("retrieveOverlayDetailDataForElement for comment", function () {
-    var map = helper.createCustomMap();
-    helper.createCommentDbOverlay(map);
-
-    var alias = helper.createAlias(map);
-    alias.setId(329157);
-    map.getModel().addAlias(alias);
-
-    var ie = new IdentifiedElement(alias);
-
-    return map.retrieveOverlayDetailDataForElement(ie, {
-      comment: true
-    }).then(function (details) {
-      assert.ok(details);
-      assert.equal(details.length, 1);
-      assert.equal(details[0].length, 1);
-      assert.ok(details[0][0] instanceof Comment);
-    });
-  });
-
-  it("getOverlayDataForIdentifiedElement", function () {
-    var map = helper.createCustomMap();
-    var commentOverlay = helper.createCommentDbOverlay(map);
-
-    var alias = helper.createAlias(map);
-    alias.setId(329157);
-    map.getModel().addAlias(alias);
-
-    var ie = new IdentifiedElement(alias);
-
-    return map.getOverlayDataForIdentifiedElement(ie, {
-      comment: true
-    }).then(function (details) {
-      assert.equal(details.length, 1);
-
-      var overlayData = details[0];
-      assert.equal(overlayData.overlay, commentOverlay);
-      assert.ok(overlayData.data);
-      assert.equal(overlayData.data.length, 1);
-      assert.ok(overlayData.data[0] instanceof Comment);
-    });
-  });
-
-  it("openSubmap", function () {
-    var options = helper.createCustomMapOptions();
-
-    var submodel = helper.createModel();
-    options.getProject().addModel(submodel);
-
-    var map = new CustomMap(options);
-
-    map.openSubmap(submodel.getId());
-    map.destroy();
-  });
-
-  describe("setCenter", function () {
-    it("default", function () {
-      var options = helper.createCustomMapOptions();
-
-      var map = new CustomMap(options);
-
-      map.setCenter(new Point(10, 20));
-      assert.ok(ServerConnector.getSessionData().getCenter(map.getModel()));
-    });
-
-    it("on submap", function () {
-      var options = helper.createCustomMapOptions();
-
-      var submodel = helper.createModel();
-      options.getProject().addModel(submodel);
-
-      var map = new CustomMap(options);
-      map.openSubmap(submodel.getId());
-      var submap = map.getSubmapById(submodel.getId());
-      submap.setCenter(new Point(10, 20));
-      assert.ok(ServerConnector.getSessionData().getCenter(submodel));
-      map.destroy();
-    });
-  });
-
-  it("setZoom", function () {
-    var options = helper.createCustomMapOptions();
-
-    var map = new CustomMap(options);
-
-    map.setZoom(3);
-    assert.equal(3, ServerConnector.getSessionData().getZoomLevel(map.getModel()));
-  });
-
-  it("removeSelection when invalid model selected", function () {
-    var options = helper.createCustomMapOptions();
-
-    var map = new CustomMap(options);
-    map.setActiveSubmapId(-1);
-
-    try {
-      map.removeSelection();
-      assert.ok(false);
-    } catch (e) {
-      assert.ok(e.message.indexOf("Cannot find submap") >= 0);
-    }
-  });
-
-  describe("_openInfoWindowForIdentifiedElement", function () {
-    it("for AliasMarker", function () {
-      var map;
-      var alias, marker;
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        return map.getModel().getAliasById(329171);
-      }).then(function (result) {
-        alias = result;
-
-        var identifiedElement = new IdentifiedElement(alias);
-        identifiedElement.setIcon("empty.png");
-
-        marker = new AliasMarker({
-          element: identifiedElement,
-          map: map
-        });
-
-        return marker.init();
-      }).then(function () {
-        assert.equal(null, map.getAliasInfoWindowById(alias.getId()));
-        return map._openInfoWindowForIdentifiedElement(marker).then(function () {
-          assert.ok(map.getAliasInfoWindowById(alias.getId()));
-        });
-      });
-
-    });
-    it("for ReactionMarker", function () {
-      var map;
-      var reaction, marker;
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        return map.getModel().getReactionById(153510);
-      }).then(function (result) {
-        reaction = result;
-
-        marker = new ReactionMarker({
-          element: new IdentifiedElement(reaction),
-          map: map
-        });
-        return marker.init();
-      }).then(function () {
-        assert.equal(null, map.getReactionInfoWindowById(reaction.getId()));
-        return map._openInfoWindowForIdentifiedElement(marker);
-      }).then(function () {
-        assert.ok(map.getReactionInfoWindowById(reaction.getId()));
-      });
-
-    });
-    it("for PointMarker", function () {
-
-      var mockObject = helper.createCustomMap();
-
-      mockObject.getOverlayDataForPoint = function () {
-        return Promise.resolve([]);
-      };
-
-      var point = new Point(2, 3.45);
-      var pointData = new PointData(point, mockObject.getId());
-
-      var pointMarker = new PointMarker({
-        element: new IdentifiedElement(pointData),
-        map: mockObject
-      });
-
-      return pointMarker.init().then(function () {
-        assert.equal(null, mockObject.getPointInfoWindowById(pointData.getId()));
-        return mockObject._openInfoWindowForIdentifiedElement(pointMarker.getIdentifiedElement());
-      }).then(function () {
-        assert.ok(mockObject.getPointInfoWindowById(pointData.getId()));
-      });
-    });
-  });
-
-  describe("init", function () {
-    it("invalid background overlay", function () {
-      var options = helper.createCustomMapOptions();
-      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay("bla");
-      var map = new CustomMap(options);
-      return map.init().then(function () {
-        assert.false("Error expected");
-      }, function () {
-        map.destroy();
-      })
-    });
-
-    it("non-existing background overlay", function () {
-      var options = helper.createCustomMapOptions();
-      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay(-1);
-      var map = new CustomMap(options);
-      return map.init().then(function () {
-        assert.ok(ServerConnector.getSessionData(options.getProject()).getSelectedBackgroundOverlay() > 0);
-        return map.destroy();
-      })
-    });
-
-    it("with session data overlays", function () {
-      var project = helper.createProject();
-      var overlay1 = helper.createOverlay();
-      overlay1.setInputDataAvailable(true);
-      project.addDataOverlay(overlay1);
-
-      var options = helper.createCustomMapOptions(project);
-      ServerConnector.getSessionData(options.getProject()).setVisibleOverlays([overlay1.getId()]);
-      var map = new CustomMap(options);
-      return map.init().then(function () {
-        return map.getVisibleDataOverlays();
-      }).then(function (overlays) {
-        assert.equal(1, overlays.length);
-        map.destroy();
-      })
-    });
-
-  });
-
-  describe("appendElementsPointingToSubmap", function () {
-    it("point to reaction", function () {
-      helper.setUrl("http://test/?id=complex_model_with_submaps");
-      return ServerConnector.getProject().then(function (project) {
-        var map = helper.createCustomMap(project);
-        var oc = helper.createSearchDbOverlay(map);
-
-        var reaction = new IdentifiedElement({id: 161955, modelId: 16729, type: "REACTION"});
-
-        return map.appendElementsPointingToSubmap([reaction], oc).then(function (elements) {
-          for (var i = 0; i < elements.length; i++) {
-            var element = elements[i];
-            //alias pointing to reaction should have an icon
-            if (element.type === "ALIAS") {
-              assert.ok(element.getIcon() !== undefined);
-            }
-          }
-        });
-      });
-    });
-  });
-  describe("logo link", function () {
-    it("left logo click", function () {
-      var map = helper.createCustomMap();
-      return map.getControl(ControlType.LOGO_2_IMG).onclick()
-    });
-    it("right logo click", function () {
-      var map = helper.createCustomMap();
-      return map.getControl(ControlType.LOGO_IMG).onclick()
-    });
-  });
-});
+"use strict";
+
+var Promise = require("bluebird");
+
+require("../mocha-config.js");
+
+var AliasMarker = require('../../../main/js/map/marker/AliasMarker');
+var AliasSurface = require('../../../main/js/map/surface/AliasSurface');
+var Comment = require('../../../main/js/map/data/Comment');
+var ControlType = require('../../../main/js/map/ControlType');
+var CustomMap = require('../../../main/js/map/CustomMap');
+var IdentifiedElement = require('../../../main/js/map/data/IdentifiedElement');
+var MapContextMenu = require('../../../main/js/gui/MapContextMenu');
+var MolArt = require('../../../main/js/map/structure/MolArt');
+var Point = require('../../../main/js/map/canvas/Point');
+var PointData = require('../../../main/js/map/data/PointData');
+var PointMarker = require('../../../main/js/map/marker/PointMarker');
+var ReactionMarker = require('../../../main/js/map/marker/ReactionMarker');
+var ReactionSurface = require('../../../main/js/map/surface/ReactionSurface');
+
+var ServerConnector = require('./../ServerConnector-mock');
+
+var logger = require('./../logger');
+
+var chai = require('chai');
+var assert = chai.assert;
+
+describe('CustomMap', function () {
+  describe("constructor", function () {
+    it("default", function () {
+      var options = helper.createCustomMapOptions();
+      var map = new CustomMap(options);
+      assert.ok(map);
+    });
+
+    it("with submaps", function () {
+      var options = helper.createCustomMapOptions();
+
+      options.getProject().addModel(helper.createModel());
+
+      var map = new CustomMap(options);
+      assert.ok(map);
+    });
+
+    it("with session data pointing to not existing overlay", function () {
+      var options = helper.createCustomMapOptions();
+
+      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay("-1");
+
+      var map = new CustomMap(options);
+      assert.ok(map);
+    });
+  });
+
+  it("getSubmapById", function () {
+    var map = helper.createCustomMap();
+    assert.ok(map.getSubmapById(map.getId()));
+  });
+
+  it("getSubmapById (invalid)", function () {
+    var map = helper.createCustomMap();
+    assert.equal(map.getSubmapById(-1), null);
+  });
+
+  it("getSubmapById (string id)", function () {
+    var map = helper.createCustomMap();
+    assert.ok(map.getSubmapById(map.getId() + ""));
+  });
+
+  describe("openDataOverlay", function () {
+    it("for not existing id", function () {
+      var map = helper.createCustomMap();
+      try {
+        map.openDataOverlay(-1);
+        assert.ok(false);
+      } catch (exception) {
+        assert.ok(exception.message.indexOf("You have no privileges") >= 0);
+      }
+    });
+
+    it("for int id", function () {
+      var options = helper.createCustomMapOptions();
+      var layout = options.getProject().getDataOverlays()[0];
+      var map = new CustomMap(options);
+      map.openDataOverlay(layout.getId());
+      assert.equal(logger.getErrors().length, 0);
+    });
+
+    it("for overlay object", function () {
+      var options = helper.createCustomMapOptions();
+      var layout = options.getProject().getDataOverlays()[0];
+      var map = new CustomMap(options);
+      map.openDataOverlay(layout);
+      assert.equal(logger.getErrors().length, 0);
+    });
+
+    it("for string id", function () {
+      var options = helper.createCustomMapOptions();
+      var layout = options.getProject().getDataOverlays()[0];
+      var map = new CustomMap(options);
+      map.openDataOverlay(layout.getId() + "");
+      assert.equal(logger.getErrors().length, 0);
+    });
+
+    it("with background overlay", function () {
+      var map = helper.createCustomMap();
+      var layout = helper.createOverlay();
+      layout.setInitialized(true);
+      layout.setInputDataAvailable(false);
+      map.getProject().addDataOverlay(layout);
+
+      return map.openDataOverlay(layout.getId()).then(function () {
+        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
+        assert.equal(0, vLayouts.length);
+      });
+    });
+
+    it("with non background overlay", function () {
+      var project = helper.createProject();
+      var layout = helper.createOverlay();
+      layout.setInitialized(true);
+      layout.setInputDataAvailable(true);
+      project.addDataOverlay(layout);
+
+      var emptyBackground = helper.createOverlay();
+      emptyBackground.setInputDataAvailable(false);
+      emptyBackground.setName("Empty");
+      project.addDataOverlay(emptyBackground);
+
+      var map = helper.createCustomMap(project);
+
+
+      return map.openDataOverlay(layout.getId()).then(function () {
+        var backgroundId = ServerConnector.getSessionData(project).getSelectedBackgroundOverlay();
+        assert.equal(backgroundId, emptyBackground.getId());
+      });
+    });
+
+    it("check backgroundChangeListener", function () {
+      var project = helper.createProject();
+      var overlay = helper.createOverlay();
+      overlay.setInitialized(true);
+      overlay.setInputDataAvailable(true);
+      project.addDataOverlay(overlay);
+
+      var overlay2 = helper.createOverlay();
+      overlay2.setInitialized(true);
+      overlay2.setInputDataAvailable(true);
+      project.addDataOverlay(overlay2);
+
+      var emptyBackground = helper.createOverlay();
+      emptyBackground.setInputDataAvailable(false);
+      emptyBackground.setName("Empty");
+      project.addDataOverlay(emptyBackground);
+
+      var map = helper.createCustomMap(project);
+
+      var counter = 0;
+      map.addListener("onBackgroundOverlayChange", function () {
+        counter++;
+      });
+
+      return map.openDataOverlay(overlay.getId()).then(function () {
+        assert.equal(1, counter, "listener wasn't fired");
+        return map.openDataOverlay(overlay2.getId());
+      }).then(function () {
+        assert.equal(1, counter, "listener shouldn't be fired again because nothing changed");
+      });
+    });
+
+    it("simple", function () {
+      var map = helper.createCustomMap();
+      var alias = helper.createAlias(map);
+      map.getModel().addAlias(alias);
+
+      var reaction = helper.createReaction(map);
+      map.getModel().addReaction(reaction);
+
+      var layout = helper.createOverlay();
+      layout.setInputDataAvailable(true);
+      layout.setInitialized(true);
+      var layoutAlias = helper.createLayoutAlias(alias);
+      layout.addAlias(layoutAlias);
+
+      var layoutReaction = helper.createLayoutReaction(reaction);
+      layout.addReaction(layoutReaction);
+
+      map.getProject().addDataOverlay(layout);
+
+      return map.openDataOverlay(layout.getId()).then(function () {
+        return map._showSelectedDataOverlay(layout.getId(), 0, 1);
+      }).then(function () {
+        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
+        assert.equal(1, vLayouts.length);
+        assert.equal(layout.getId(), vLayouts[0]);
+
+        assert.equal(2, map.selectedLayoutOverlays[layout.getId()].length);
+        var surface1 = map.selectedLayoutOverlays[layout.getId()][0];
+        var surface2 = map.selectedLayoutOverlays[layout.getId()][1];
+        assert.ok(surface1 instanceof AliasSurface || surface2 instanceof AliasSurface);
+        assert.ok(surface1 instanceof ReactionSurface || surface2 instanceof ReactionSurface);
+
+        // now hide the layout
+        return map._hideSelectedLayout(layout.getId());
+      }).then(function () {
+        assert.ok(map.selectedLayoutOverlays[layout.getId()]);
+        assert.equal(0, map.selectedLayoutOverlays[layout.getId()].length);
+      });
+
+    });
+
+    it("with submaps", function () {
+      var projectId = "complex_model_with_submaps";
+      helper.setUrl("http://test/?id=" + projectId);
+      var customMap;
+      var emptySubmodelId = 16730;
+      var filledSubmodelId = 16731;
+      var overlayId = 18083;
+
+      return ServerConnector.getProject(projectId).then(function (project) {
+        var options = helper.createCustomMapOptions(project);
+        customMap = new CustomMap(options);
+
+        return customMap.openSubmap(emptySubmodelId);
+      }).then(function () {
+        return customMap.openSubmap(filledSubmodelId);
+      }).then(function () {
+
+        var emptySubmap = customMap.getSubmapById(emptySubmodelId);
+        var filledSubmap = customMap.getSubmapById(filledSubmodelId);
+
+        return customMap.openDataOverlay(overlayId).then(function () {
+
+          assert.equal(2, customMap.selectedLayoutOverlays[overlayId].length);
+          assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
+          assert.equal(0, emptySubmap.selectedLayoutOverlays[overlayId].length);
+
+          // now hide the layout
+
+          return customMap.hideSelectedLayout(overlayId);
+        }).then(function () {
+          assert.equal(0, customMap.selectedLayoutOverlays[overlayId].length);
+          assert.equal(0, filledSubmap.selectedLayoutOverlays[overlayId].length);
+          assert.equal(0, emptySubmap.selectedLayoutOverlays[overlayId].length);
+        });
+
+      }).then(function () {
+        return customMap.destroy();
+      });
+    });
+
+    it("with submap double opened", function () {
+      var projectId = "complex_model_with_submaps";
+      helper.setUrl("http://test/?id=" + projectId);
+      var customMap, filledSubmap;
+      var filledSubmodelId = 16731;
+      var overlayId = 18083;
+
+      return ServerConnector.getProject(projectId).then(function (project) {
+        customMap = helper.createCustomMap(project);
+        return customMap.openSubmap(filledSubmodelId);
+      }).then(function () {
+        filledSubmap = customMap.getSubmapById(filledSubmodelId);
+        return customMap.openDataOverlay(overlayId);
+      }).then(function () {
+        assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
+        return customMap.openSubmap(filledSubmodelId);
+      }).then(function () {
+        //after opening submap nothing should change
+        assert.equal(1, filledSubmap.selectedLayoutOverlays[overlayId].length);
+      }).finally(function () {
+        return customMap.destroy();
+      });
+    });
+
+    it("check if info window content changed", function () {
+      var project = helper.createProject();
+      var layout = helper.createOverlay();
+      layout.setInitialized(true);
+      layout.setInputDataAvailable(true);
+      project.addDataOverlay(layout);
+
+      var map = helper.createCustomMap(project);
+
+      var alias = helper.createAlias(map);
+      var ie = helper.createIdentifiedElement(alias);
+      var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
+      var infoWindow;
+      return map.openInfoWindowForIdentifiedElement(ie, marker).then(function (result) {
+        infoWindow = result;
+        assert.notOk(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
+        return map.openDataOverlay(layout.getId());
+      }).then(function () {
+        assert.ok(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
+        assert.equal(logger.getErrors().length, 0);
+      });
+    });
+  });
+
+  describe("hideDataOverlay", function () {
+    it("default", function () {
+      var map = helper.createCustomMap();
+      var layout = helper.createOverlay();
+      layout.setInitialized(true);
+      layout.setInputDataAvailable(true);
+      map.getProject().addDataOverlay(layout);
+
+      return map.openDataOverlay(layout.getId()).then(function () {
+        return map.hideDataOverlay(layout.getId());
+      }).then(function () {
+        var vLayouts = ServerConnector.getSessionData(map.getProject()).getVisibleOverlays();
+        assert.equal(0, vLayouts.length);
+      });
+    });
+    it("check if info window content changed", function () {
+      var project = helper.createProject();
+      var layout = helper.createOverlay();
+      layout.setInitialized(true);
+      layout.setInputDataAvailable(true);
+      project.addDataOverlay(layout);
+
+      var map = helper.createCustomMap(project);
+
+      var alias = helper.createAlias(map);
+      var ie = helper.createIdentifiedElement(alias);
+      var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
+      var infoWindow;
+      return map.openInfoWindowForIdentifiedElement(ie, marker).then(function (result) {
+        infoWindow = result;
+        return map.openDataOverlay(layout.getId());
+      }).then(function () {
+        assert.ok(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
+        return map.hideDataOverlay(layout.getId());
+      }).then(function () {
+        assert.notOk(infoWindow.getContent().innerHTML.indexOf(layout.getName()) >= 0);
+        assert.equal(logger.getErrors().length, 0);
+      });
+    });
+  });
+
+
+  describe("renderOverlayCollection", function () {
+    it("for alias", function () {
+      var map = helper.createCustomMap();
+      var alias = helper.createAlias(map);
+
+      var oc = helper.createDbOverlay(map);
+
+      oc.getIdentifiedElements = function () {
+        return Promise.resolve([new IdentifiedElement({
+          objectId: alias.getId(),
+          icon: "empty.png",
+          modelId: map.getId(),
+          type: "Alias"
+        })]);
+      };
+      return map.renderOverlayCollection({
+        overlayCollection: oc
+      }).then(function () {
+        var markers = map.getMarkerSurfaceCollection().getMarkers();
+        assert.equal(1, markers.length);
+      });
+    });
+
+    it("alias re-rendering with different icon", function () {
+      var map = helper.createCustomMap();
+      var reaction = helper.createReaction(map, true);
+      var alias = helper.createAlias(map);
+      map.getModel().addAlias(alias);
+      map.getModel().addReaction(reaction);
+
+      var identifiedElement = new IdentifiedElement(alias);
+      identifiedElement.setIcon("empty.png");
+      var identifiedElement2 = new IdentifiedElement(alias);
+      identifiedElement2.setIcon("new_icon.png");
+
+      var marker;
+      var oc = helper.createDbOverlay(map);
+
+      oc.getIdentifiedElements = function () {
+        return Promise.resolve([identifiedElement, new IdentifiedElement(reaction)]);
+      };
+      return map.renderOverlayCollection({
+        overlayCollection: oc
+      }).then(function () {
+        marker = map.getMarkerSurfaceCollection().getMarker(identifiedElement);
+        oc.getIdentifiedElements = function () {
+          return Promise.resolve([identifiedElement2]);
+        };
+        assert.ok(map.getMarkerSurfaceCollection().getSurface({element: reaction, overlay: oc}));
+
+        return map.renderOverlayCollection({
+          overlayCollection: oc
+        });
+      }).then(function () {
+        assert.equal(marker.getIcon(), "new_icon.png");
+
+        assert.notOk(oc.mapOverlays["REACTION"][reaction.getId()]);
+      });
+    });
+
+    it("for set of aliases", function () {
+      var map = helper.createCustomMap();
+
+      var oc = helper.createDbOverlay(map);
+
+      var identifiedElements = [];
+      for (var i = 0; i < 3; i++) {
+        var alias = helper.createAlias(map);
+        map.getModel().addAlias(alias);
+        var ie = helper.createIdentifiedElement(alias);
+        ie.setIcon("");
+        identifiedElements.push(ie);
+      }
+
+      oc.getIdentifiedElements = function () {
+        return Promise.resolve(identifiedElements);
+      };
+
+      return map.renderOverlayCollection({
+        overlayCollection: oc
+      }).then(function () {
+        var markers = map.getMarkerSurfaceCollection().getMarkers();
+        assert.equal(3, markers.length);
+      });
+    });
+
+    it("for point", function () {
+      var map = helper.createCustomMap();
+
+      var oc = helper.createDbOverlay(map);
+
+      var javaObj = {
+        objectId: "Point2D.Double[117.685546875, 204.6923828125001]",
+        modelId: map.getId(),
+        type: "POINT",
+        icon: "marker/empty.png"
+      };
+
+      oc.getIdentifiedElements = function () {
+        return Promise.resolve([new IdentifiedElement(javaObj)]);
+      };
+
+      return map.renderOverlayCollection({
+        overlayCollection: oc
+      }).then(function () {
+        var markers = map.getMarkerSurfaceCollection().getMarkers();
+        assert.equal(1, markers.length);
+      });
+    });
+
+    it("for reaction", function () {
+      var map = helper.createCustomMap();
+      var reaction = helper.createReaction(map, true);
+      map.getModel().addReaction(reaction);
+
+      var oc = helper.createDbOverlay(map);
+
+      oc.getIdentifiedElements = function () {
+        return Promise.resolve([new IdentifiedElement(reaction)]);
+      };
+
+      return map.renderOverlayCollection({
+        overlayCollection: oc
+      }).then(function () {
+        var surfaces = map.getMarkerSurfaceCollection().getSurfaces();
+        assert.equal(1, surfaces.length);
+      });
+    });
+  });
+
+  it("refreshMarkers", function () {
+    var map = helper.createCustomMap();
+    var alias = helper.createAlias(map);
+    map.getModel().addAlias(alias);
+
+    var oc = helper.createDbOverlay(map);
+
+    oc.getIdentifiedElements = function () {
+      var element = new IdentifiedElement(alias);
+      element.setIcon("icon");
+      return Promise.resolve([element]);
+    };
+    return map.renderOverlayCollection({
+      overlayCollection: oc
+    }).then(function () {
+      return map.refreshMarkers(true);
+    }).then(function () {
+      var markers = map.getMarkerSurfaceCollection().getMarkers();
+      assert.equal(1, markers.length);
+    });
+  });
+
+  it("clearDbOverlays", function () {
+    var map = helper.createCustomMap();
+
+    var oc = helper.createDbOverlay(map);
+
+    var javaObj = {
+      objectId: "Point2D.Double[117.685546875, 204.6923828125001]",
+      modelId: map.getId(),
+      type: "POINT",
+      icon: "marker/empty.png"
+    };
+    var searchResults = [new IdentifiedElement(javaObj)];
+
+    oc.getIdentifiedElements = function () {
+      return Promise.resolve(searchResults);
+    };
+    oc.clear = function () {
+      searchResults = [];
+      return this.callListeners("onSearch", searchResults);
+    };
+
+    return map.renderOverlayCollection({
+      overlayCollection: oc
+    }).then(function () {
+
+      return map.clearDbOverlays();
+    }).then(function () {
+      var markerCount = 0;
+      for (var id in oc.pointMarkers) {
+        if (oc.pointMarkers.hasOwnProperty(id)) {
+          markerCount++;
+        }
+      }
+
+      assert.equal(0, markerCount);
+    });
+
+  });
+
+  it("getInfoWindowForIdentifiedElement ( reaction)", function () {
+    var map = helper.createCustomMap();
+    var reaction = helper.createReaction(map, true);
+    map.getModel().addReaction(reaction);
+
+    var ie = helper.createIdentifiedElement(reaction);
+    var marker = map.getMapCanvas().createMarker({icon: "empty.png", position: new Point(0, 0)});
+
+    var infoWindow = map.getInfoWindowForIdentifiedElement(ie);
+
+    assert.equal(null, infoWindow);
+    return map.openInfoWindowForIdentifiedElement(ie, marker).then(function () {
+      infoWindow = map.getInfoWindowForIdentifiedElement(ie);
+
+      assert.ok(infoWindow);
+    });
+
+  });
+
+  it("right click on map", function () {
+    var map = helper.createCustomMap();
+    map.setContextMenu(new MapContextMenu({
+      customMap: map,
+      element: testDiv,
+      molArt: new MolArt(map.getElement(), map)
+    }));
+
+    return map.getContextMenu().init().then(function () {
+      var data = {
+        stop: null,
+        point: new Point(10.0, 20.0)
+      };
+
+      assert.notOk(map.getActiveSubmapId());
+      return map.triggerEvent("map-rightclick", data);
+    }).then(function () {
+      //we need to wait because some implementations don't use promises...
+      return Promise.delay(100);
+    }).then(function () {
+      assert.equal(map.getId(), map.getActiveSubmapId());
+      return map.destroy();
+    });
+
+  });
+
+  it("left click on map", function () {
+    var map;
+    var searchOverlay;
+    return ServerConnector.getProject().then(function (project) {
+      map = helper.createCustomMap(project);
+      searchOverlay = helper.createSearchDbOverlay(map);
+
+      var data = {
+        stop: null,
+        point: new Point(184.79, 365.76)
+      };
+      map.setZoom(4);
+
+      assert.notOk(map.getActiveSubmapId());
+      return map.triggerEvent("map-click", data);
+    }).then(function () {
+      //we need to wait because some implementations don't use promises...
+      return Promise.delay(100);
+    }).then(function () {
+      assert.equal(map.getId(), map.getActiveSubmapId());
+      var element = new IdentifiedElement({id: 329171, type: "ALIAS", modelId: map.getId()});
+      assert.ok(map.getMarkerSurfaceCollection().getMarker(element));
+    });
+
+  });
+
+  it("left click on reaction", function () {
+    var map;
+    var searchOverlay;
+    return ServerConnector.getProject().then(function (project) {
+      map = helper.createCustomMap(project);
+      searchOverlay = helper.createSearchDbOverlay(map);
+
+      var data = {
+        stop: null,
+        point: new Point(457.51, 356.84)
+      };
+      map.setZoom(4);
+
+      assert.notOk(map.getActiveSubmapId());
+      return map.triggerEvent("map-click", data);
+    }).then(function () {
+      //we need to wait because some implementations don't use promises...
+      return Promise.delay(100);
+    }).then(function () {
+      assert.equal(map.getId(), map.getActiveSubmapId());
+
+      var reaction = new IdentifiedElement({id: 153521, type: "REACTION", modelId: map.getId()});
+      var surface = map.getMarkerSurfaceCollection().getSurface({element: reaction, overlay: searchOverlay});
+
+      assert.ok(surface);
+      assert.ok(surface.isShown());
+
+      var element = new IdentifiedElement({id: 329165, type: "ALIAS", modelId: map.getId()});
+      var marker = map.getMarkerSurfaceCollection().getMarker(element);
+      assert.ok(marker);
+    });
+  });
+
+  it("changed coordinates in map", function () {
+    var map = helper.createCustomMap();
+    var oldCenter = map.getCenter();
+    var newCenter = new Point(3, 87);
+    map.setCenter(newCenter);
+    map.triggerEvent("map-center_changed");
+
+    var center = ServerConnector.getSessionData(map.getProject()).getCenter(map.getModel());
+    assert.ok(center !== oldCenter);
+    assert.ok(center instanceof Point);
+  });
+
+  it("refreshComments", function () {
+    var map = helper.createCustomMap();
+    map.getModel().setId(15781);
+    helper.createCommentDbOverlay(map);
+
+    ServerConnector.getSessionData().setShowComments(true);
+    return map.refreshComments().then(function () {
+      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
+        id: '(241.01,372.35)',
+        modelId: map.getId(),
+        type: "POINT"
+      })));
+      assert.ok(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
+        id: '(643.96,144.09)',
+        modelId: map.getId(),
+        type: "POINT"
+      })));
+      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
+        id: '(216.65,370.00)',
+        modelId: map.getId(),
+        type: "POINT"
+      })));
+      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
+        id: 'unkId',
+        modelId: map.getId(),
+        type: "POINT"
+      })));
+    });
+  });
+
+  it("hide comments", function () {
+    var map = helper.createCustomMap();
+    map.getModel().setId(15781);
+    helper.createCommentDbOverlay(map);
+
+    ServerConnector.getSessionData().setShowComments(true);
+    return map.refreshComments().then(function () {
+      ServerConnector.getSessionData().setShowComments(false);
+      return map.refreshComments();
+    }).then(function () {
+      assert.notOk(map.getMarkerSurfaceCollection().getMarker(new IdentifiedElement({
+        id: '(241.01, 372.35)',
+        modelId: map.getId(),
+        type: "POINT"
+      })));
+    });
+  });
+
+  it("openCommentDialog", function () {
+    var map = helper.createCustomMap();
+    map.getModel().setId(15781);
+    map.setActiveSubmapId(15781);
+    map.setActiveSubmapClickCoordinates(new Point(2, 12));
+    return map.openCommentDialog().then(function () {
+      var types = map.getCommentDialog().getTypes();
+      assert.equal(types.length, 6);
+      var selected = map.getCommentDialog().getSelectedType();
+      assert.ok(selected === "<General>");
+
+      map.getCommentDialog().setSelectedType(1);
+      selected = map.getCommentDialog().getSelectedType();
+      assert.notOk(selected === "<General>");
+
+      map.getCommentDialog().setSelectedType(2);
+      selected = map.getCommentDialog().getSelectedType();
+      assert.notOk(selected === "<General>");
+    }).then(function () {
+      map.getCommentDialog().destroy();
+    });
+  });
+
+  describe("addComment", function () {
+    it("default", function () {
+      var map;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        helper.createCommentDbOverlay(map);
+        map.setActiveSubmapId(map.getId());
+        map.setActiveSubmapClickCoordinates(new Point(2, 12));
+        return map.openCommentDialog();
+      }).then(function () {
+        return map.getCommentDialog().addComment();
+      }).then(function (comments) {
+        assert.equal(0, comments.length);
+        map.getCommentDialog().destroy();
+      });
+    });
+
+    it("when comments are visible", function () {
+      var map;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        helper.createCommentDbOverlay(map);
+        map.setActiveSubmapId(map.getId());
+        map.setActiveSubmapClickCoordinates(new Point(2, 12));
+        return map.openCommentDialog();
+      }).then(function () {
+        ServerConnector.getSessionData().setShowComments(true);
+        return map.getCommentDialog().addComment();
+      }).then(function (comments) {
+        assert.ok(comments.length > 0);
+        map.getCommentDialog().destroy();
+      });
+    });
+
+    it("add for protein", function () {
+      var map;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        helper.createCommentDbOverlay(map);
+        map.setActiveSubmapId(map.getId());
+        map.setActiveSubmapClickCoordinates(new Point(2, 12));
+        return map.openCommentDialog();
+      }).then(function () {
+        var dialog = map.getCommentDialog();
+        dialog.setSelectedType(1);
+        return dialog.addComment();
+      }).then(function () {
+        map.getCommentDialog().destroy();
+      });
+    });
+  });
+
+  it("retrieveOverlayDetailDataForElement for comment", function () {
+    var map = helper.createCustomMap();
+    helper.createCommentDbOverlay(map);
+
+    var alias = helper.createAlias(map);
+    alias.setId(329157);
+    map.getModel().addAlias(alias);
+
+    var ie = new IdentifiedElement(alias);
+
+    return map.retrieveOverlayDetailDataForElement(ie, {
+      comment: true
+    }).then(function (details) {
+      assert.ok(details);
+      assert.equal(details.length, 1);
+      assert.equal(details[0].length, 1);
+      assert.ok(details[0][0] instanceof Comment);
+    });
+  });
+
+  it("getOverlayDataForIdentifiedElement", function () {
+    var map = helper.createCustomMap();
+    var commentOverlay = helper.createCommentDbOverlay(map);
+
+    var alias = helper.createAlias(map);
+    alias.setId(329157);
+    map.getModel().addAlias(alias);
+
+    var ie = new IdentifiedElement(alias);
+
+    return map.getOverlayDataForIdentifiedElement(ie, {
+      comment: true
+    }).then(function (details) {
+      assert.equal(details.length, 1);
+
+      var overlayData = details[0];
+      assert.equal(overlayData.overlay, commentOverlay);
+      assert.ok(overlayData.data);
+      assert.equal(overlayData.data.length, 1);
+      assert.ok(overlayData.data[0] instanceof Comment);
+    });
+  });
+
+  it("openSubmap", function () {
+    var options = helper.createCustomMapOptions();
+
+    var submodel = helper.createModel();
+    options.getProject().addModel(submodel);
+
+    var map = new CustomMap(options);
+
+    map.openSubmap(submodel.getId());
+    map.destroy();
+  });
+
+  describe("setCenter", function () {
+    it("default", function () {
+      var options = helper.createCustomMapOptions();
+
+      var map = new CustomMap(options);
+
+      map.setCenter(new Point(10, 20));
+      assert.ok(ServerConnector.getSessionData().getCenter(map.getModel()));
+    });
+
+    it("on submap", function () {
+      var options = helper.createCustomMapOptions();
+
+      var submodel = helper.createModel();
+      options.getProject().addModel(submodel);
+
+      var map = new CustomMap(options);
+      map.openSubmap(submodel.getId());
+      var submap = map.getSubmapById(submodel.getId());
+      submap.setCenter(new Point(10, 20));
+      assert.ok(ServerConnector.getSessionData().getCenter(submodel));
+      map.destroy();
+    });
+  });
+
+  it("setZoom", function () {
+    var options = helper.createCustomMapOptions();
+
+    var map = new CustomMap(options);
+
+    map.setZoom(3);
+    assert.equal(3, ServerConnector.getSessionData().getZoomLevel(map.getModel()));
+  });
+
+  it("removeSelection when invalid model selected", function () {
+    var options = helper.createCustomMapOptions();
+
+    var map = new CustomMap(options);
+    map.setActiveSubmapId(-1);
+
+    try {
+      map.removeSelection();
+      assert.ok(false);
+    } catch (e) {
+      assert.ok(e.message.indexOf("Cannot find submap") >= 0);
+    }
+  });
+
+  describe("_openInfoWindowForIdentifiedElement", function () {
+    it("for AliasMarker", function () {
+      var map;
+      var alias, marker;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        return map.getModel().getAliasById(329171);
+      }).then(function (result) {
+        alias = result;
+
+        var identifiedElement = new IdentifiedElement(alias);
+        identifiedElement.setIcon("empty.png");
+
+        marker = new AliasMarker({
+          element: identifiedElement,
+          map: map
+        });
+
+        return marker.init();
+      }).then(function () {
+        assert.equal(null, map.getAliasInfoWindowById(alias.getId()));
+        return map._openInfoWindowForIdentifiedElement(marker).then(function () {
+          assert.ok(map.getAliasInfoWindowById(alias.getId()));
+        });
+      });
+
+    });
+    it("for ReactionMarker", function () {
+      var map;
+      var reaction, marker;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        return map.getModel().getReactionById(153510);
+      }).then(function (result) {
+        reaction = result;
+
+        marker = new ReactionMarker({
+          element: new IdentifiedElement(reaction),
+          map: map
+        });
+        return marker.init();
+      }).then(function () {
+        assert.equal(null, map.getReactionInfoWindowById(reaction.getId()));
+        return map._openInfoWindowForIdentifiedElement(marker);
+      }).then(function () {
+        assert.ok(map.getReactionInfoWindowById(reaction.getId()));
+      });
+
+    });
+    it("for PointMarker", function () {
+
+      var mockObject = helper.createCustomMap();
+
+      mockObject.getOverlayDataForPoint = function () {
+        return Promise.resolve([]);
+      };
+
+      var point = new Point(2, 3.45);
+      var pointData = new PointData(point, mockObject.getId());
+
+      var pointMarker = new PointMarker({
+        element: new IdentifiedElement(pointData),
+        map: mockObject
+      });
+
+      return pointMarker.init().then(function () {
+        assert.equal(null, mockObject.getPointInfoWindowById(pointData.getId()));
+        return mockObject._openInfoWindowForIdentifiedElement(pointMarker.getIdentifiedElement());
+      }).then(function () {
+        assert.ok(mockObject.getPointInfoWindowById(pointData.getId()));
+      });
+    });
+  });
+
+  describe("init", function () {
+    it("invalid background overlay", function () {
+      var options = helper.createCustomMapOptions();
+      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay("bla");
+      var map = new CustomMap(options);
+      return map.init().then(function () {
+        assert.false("Error expected");
+      }, function () {
+        map.destroy();
+      })
+    });
+
+    it("non-existing background overlay", function () {
+      var options = helper.createCustomMapOptions();
+      ServerConnector.getSessionData(options.getProject()).setSelectedBackgroundOverlay(-1);
+      var map = new CustomMap(options);
+      return map.init().then(function () {
+        assert.ok(ServerConnector.getSessionData(options.getProject()).getSelectedBackgroundOverlay() > 0);
+        return map.destroy();
+      })
+    });
+
+    it("with session data overlays", function () {
+      var project = helper.createProject();
+      var overlay1 = helper.createOverlay();
+      overlay1.setInputDataAvailable(true);
+      project.addDataOverlay(overlay1);
+
+      var options = helper.createCustomMapOptions(project);
+      ServerConnector.getSessionData(options.getProject()).setVisibleOverlays([overlay1.getId()]);
+      var map = new CustomMap(options);
+      return map.init().then(function () {
+        return map.getVisibleDataOverlays();
+      }).then(function (overlays) {
+        assert.equal(1, overlays.length);
+        map.destroy();
+      })
+    });
+
+  });
+
+  describe("appendElementsPointingToSubmap", function () {
+    it("point to reaction", function () {
+      helper.setUrl("http://test/?id=complex_model_with_submaps");
+      return ServerConnector.getProject().then(function (project) {
+        var map = helper.createCustomMap(project);
+        var oc = helper.createSearchDbOverlay(map);
+
+        var reaction = new IdentifiedElement({id: 161955, modelId: 16729, type: "REACTION"});
+
+        return map.appendElementsPointingToSubmap([reaction], oc).then(function (elements) {
+          for (var i = 0; i < elements.length; i++) {
+            var element = elements[i];
+            //alias pointing to reaction should have an icon
+            if (element.type === "ALIAS") {
+              assert.ok(element.getIcon() !== undefined);
+            }
+          }
+        });
+      });
+    });
+  });
+  describe("logo link", function () {
+    it("left logo click", function () {
+      var map = helper.createCustomMap();
+      return map.getControl(ControlType.LOGO_2_IMG).onclick()
+    });
+    it("right logo click", function () {
+      var map = helper.createCustomMap();
+      return map.getControl(ControlType.LOGO_IMG).onclick()
+    });
+  });
+});
diff --git a/frontend-js/src/test/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas-test.js b/frontend-js/src/test/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas-test.js
index a95bd3d3adbf145d9ba74b55f38378bf4ac5eaaa..c91cf90576e5a09a1396eb0d8e05641f4c1e09f4 100644
--- a/frontend-js/src/test/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas-test.js
+++ b/frontend-js/src/test/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas-test.js
@@ -208,11 +208,41 @@ describe('GoogleMapsApiCanvas', function () {
       strokeColor: "#330000",
       strokeOpacity: 0.5,
       strokeWeight: 1.0,
-      id:"1",
+      id: "1",
       fillColor: "#00FF00",
-      bounds: new Bounds(new Point(0,0), new Point(2,2))
+      bounds: new Bounds(new Point(0, 0), new Point(2, 2))
+    });
+    return google.maps.event.trigger(rectangle.getGoogleRectangles()[0], "click");
+  });
+
+  it("set rectangle bounds", function () {
+    var canvas = new GoogleMapsApiCanvas(testDiv, testOptions);
+    var rectangle = canvas.createRectangle({
+      fillOpacity: 0.7,
+      strokeColor: "#330000",
+      strokeOpacity: 0.5,
+      strokeWeight: 1.0,
+      id: "1",
+      fillColor: "#00FF00",
+      bounds: new Bounds(new Point(10, 10), new Point(20, 200))
+    });
+    rectangle.setBounds(new Bounds(new Point(100, 10), new Point(150, 200)));
+    var newBounds = rectangle.getBounds();
+    assert.closeTo(newBounds.getTopLeft().distanceTo(new Point(100, 10)), 0, helper.EPSILON);
+    assert.closeTo(newBounds.getRightBottom().distanceTo(new Point(150, 200)), 0, helper.EPSILON);
+  });
+
+  it("create rectangle with gradient", function () {
+    var canvas = new GoogleMapsApiCanvas(testDiv, testOptions);
+    var rectangle = canvas.createRectangle({
+      fillOpacity: 0.7,
+      strokeColor: "#330000",
+      strokeOpacity: 0.5,
+      strokeWeight: 1.0,
+      id: "1",
+      fillGradient: [{color: "#00FF00", amount: 1}, {color: "#00aa00", amount: 5}],
+      bounds: new Bounds(new Point(10, 10), new Point(20, 200))
     });
-    return google.maps.event.trigger(rectangle.getGoogleRectangle(), "click");
   });
 
 });
diff --git a/frontend-js/src/test/js/map/canvas/Point-test.js b/frontend-js/src/test/js/map/canvas/Point-test.js
index bcc946c1c81c0000db065072aa7c56d2200088cb..505ec28a66ae3d41f3cabb2ef942dbdc5d027d64 100644
--- a/frontend-js/src/test/js/map/canvas/Point-test.js
+++ b/frontend-js/src/test/js/map/canvas/Point-test.js
@@ -28,5 +28,18 @@ describe('Point', function () {
       assert.equal(point.y, point2.y);
     });
   });
+  describe("distanceTo", function () {
+    it("the same", function () {
+      var point = new Point(12, 3);
+      assert.closeTo(point.distanceTo(point), 0, helper.EPSILON);
+    });
+    it("different", function () {
+      var point = new Point(12, 3);
+      var point2 = new Point(15, 7);
+      assert.closeTo(point.distanceTo(point2), 5, helper.EPSILON);
+      assert.closeTo(point.distanceTo(new Point(15, 3)), 3, helper.EPSILON);
+      assert.closeTo(point.distanceTo(new Point(12, -1)), 4, helper.EPSILON);
+    });
+  });
 
 });
diff --git a/frontend-js/src/test/js/map/surface/AliasSurface-test.js b/frontend-js/src/test/js/map/surface/AliasSurface-test.js
index 92ec4e60627ac61e2e6b09ae1a93b56ef40c7d48..4c98a6eb0db1934f67c8ddf5868744e2b0377a93 100644
--- a/frontend-js/src/test/js/map/surface/AliasSurface-test.js
+++ b/frontend-js/src/test/js/map/surface/AliasSurface-test.js
@@ -13,10 +13,9 @@ describe('AliasSurface', function () {
     it("default", function () {
       var map = helper.createCustomMap();
       var alias = helper.createAlias(map);
-      var layoutAlias = helper.createLayoutAlias(alias);
 
       var result = new AliasSurface({
-        overlayAlias: layoutAlias,
+        overlayData: [helper.createLayoutAlias(alias)],
         alias: alias,
         map: map,
         startX: 1,
@@ -30,10 +29,9 @@ describe('AliasSurface', function () {
     it("no bounds", function () {
       var map = helper.createCustomMap();
       var alias = helper.createAlias(map);
-      var layoutAlias = helper.createLayoutAlias(alias);
 
       var result = new AliasSurface({
-        overlayAlias: layoutAlias,
+        overlayData: [helper.createLayoutAlias(alias)],
         alias: alias,
         map: map
       });
@@ -55,6 +53,7 @@ describe('AliasSurface', function () {
         alias = result;
         surface = new AliasSurface({
           alias: result,
+          overlayData: [helper.createLayoutAlias(alias)],
           map: map
         });
         return surface.init();
@@ -71,7 +70,7 @@ describe('AliasSurface', function () {
       var map = helper.createCustomMap();
       var alias = helper.createAlias(map);
       var surface = new AliasSurface({
-        overlayAlias: helper.createLayoutAlias(alias),
+        overlayData: [helper.createLayoutAlias(alias)],
         alias: alias,
         map: map,
         startX: 1,
@@ -97,6 +96,7 @@ describe('AliasSurface', function () {
         alias = result;
         surface = new AliasSurface({
           alias: result,
+          overlayData: [helper.createLayoutAlias(result)],
           map: map,
           onClick: function () {
             clicked = true;
diff --git a/frontend-js/src/test/js/map/window/AliasInfoWindow-test.js b/frontend-js/src/test/js/map/window/AliasInfoWindow-test.js
index 627d901f4eb3c93642f97cfe0c1cacdfe60fa9b0..85c8f1ca153eba39ac234e5ed51849a6f58734da 100644
--- a/frontend-js/src/test/js/map/window/AliasInfoWindow-test.js
+++ b/frontend-js/src/test/js/map/window/AliasInfoWindow-test.js
@@ -246,9 +246,9 @@ describe('AliasInfoWindow', function () {
         });
         return win.init();
       }).then(function () {
-        win.layoutAliases = [layoutAlias];
-        win.layoutNames = ["xxx"];
-        return win.createGenomicDiv();
+        var overlay = helper.createOverlay();
+        overlay.addAlias(layoutAlias);
+        return win.createGenomicDiv({overlays: [overlay]});
       }).then(function (div) {
         assert.ok(div);
         assert.ok(div.innerHTML.indexOf("No reference genome data available on minerva platform") === -1);
@@ -257,41 +257,6 @@ describe('AliasInfoWindow', function () {
 
     });
 
-    it("on submap map", function () {
-      helper.setUrl("http://test/?id=complex_model_with_submaps");
-
-      var map, submap, overlay, win, alias;
-
-      return ServerConnector.getProject().then(function (project) {
-        map = helper.createCustomMap(project);
-        submap = map.getSubmapById(16731);
-        overlay = new DataOverlay(18077, "xxx");
-        overlay.setInitialized(true);
-        return map.openSubmap(submap.getId());
-      }).then(function () {
-        return submap.getModel().getAliasById(345337);
-      }).then(function (data) {
-        alias = data;
-        win = new AliasInfoWindow({
-          alias: alias,
-          map: submap,
-          marker: helper.createMarker({element: alias, map: submap})
-        });
-        return win.init();
-      }).then(function () {
-        var layoutAlias = helper.createLayoutAlias(alias);
-        layoutAlias.setType(LayoutAlias.GENETIC_VARIANT);
-        win.layoutAliases = [layoutAlias];
-        win.layoutNames = ["xxx"];
-        return win.createGenomicDiv();
-      }).then(function (div) {
-        assert.ok(div);
-        win.destroy();
-        return map.destroy();
-      });
-
-    });
-
     it("with no genetic data", function () {
       var map;
 
@@ -312,8 +277,8 @@ describe('AliasInfoWindow', function () {
         });
         return win.init();
       }).then(function () {
-        win.layoutAliases = [undefined];
-        return win.createGenomicDiv();
+        var overlays = [helper.createOverlay()];
+        return win.createGenomicDiv({overlays: overlays});
       }).then(function (div) {
         assert.ok(div);
         assert.ok(div.innerHTML.indexOf("No reference genome data available on minerva platform") === -1);
@@ -350,8 +315,9 @@ describe('AliasInfoWindow', function () {
         });
         return win.init();
       }).then(function () {
-        win.layoutAliases = [layoutAlias];
-        return win.createGenomicDiv();
+        var overlay = helper.createOverlay();
+        overlay.addAlias(layoutAlias);
+        return win.createGenomicDiv({overlays: [overlay]});
       }).then(function (div) {
         assert.ok(div);
         assert.ok(div.innerHTML.indexOf("No reference genome data available on minerva platform") >= -1);
@@ -375,28 +341,70 @@ describe('AliasInfoWindow', function () {
     assert.ok(functions.isDomElement(aliasWindow.createWaitingContentDiv()));
   });
 
-  it("createChartDiv ", function () {
-    var map, ie, aliasWindow;
-    return ServerConnector.getProject().then(function (project) {
-      map = helper.createCustomMap(project);
-      ie = new IdentifiedElement({
-        id: 329170,
-        modelId: map.getId(),
-        type: "ALIAS"
+  describe("createChartDiv ", function () {
+    it("on submap map", function () {
+      helper.setUrl("http://test/?id=complex_model_with_submaps");
+
+      var map, submap, overlay, win, aliasOnSubmap, alias;
+
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        submap = map.getSubmapById(16731);
+        overlay = new DataOverlay(18077, "xxx");
+        overlay.setInitialized(true);
+        return map.openSubmap(submap.getId());
+      }).then(function () {
+        return map.getModel().getAliasById(345339);
+      }).then(function (data) {
+        alias = data;
+        return submap.getModel().getAliasById(345337);
+      }).then(function (data) {
+        aliasOnSubmap = data;
+        win = new AliasInfoWindow({
+          alias: alias,
+          map: map,
+          marker: helper.createMarker({element: alias, map: map})
+        });
+        return win.init();
+      }).then(function () {
+        var layoutAlias = helper.createLayoutAlias(aliasOnSubmap);
+        layoutAlias.setType(LayoutAlias.GENERIC);
+        var overlay = helper.createOverlay();
+        overlay.addAlias(layoutAlias);
+        return win.createChartDiv({overlays: [overlay]});
+      }).then(function (div) {
+        assert.ok(div);
+        win.destroy();
+        return map.destroy();
       });
 
-      return map.getModel().getByIdentifiedElement(ie, true);
-    }).then(function (alias) {
-      aliasWindow = new AliasInfoWindow({
-        alias: alias,
-        map: map,
-        marker: helper.createMarker({element: alias, map: map})
+    });
+
+    it("default ", function () {
+      var map, ie, aliasWindow;
+      return ServerConnector.getProject().then(function (project) {
+        map = helper.createCustomMap(project);
+        ie = new IdentifiedElement({
+          id: 329170,
+          modelId: map.getId(),
+          type: "ALIAS"
+        });
+
+        return map.getModel().getByIdentifiedElement(ie, true);
+      }).then(function (alias) {
+        aliasWindow = new AliasInfoWindow({
+          alias: alias,
+          map: map,
+          marker: helper.createMarker({element: alias, map: map})
+        });
+        var overlay = helper.createOverlay();
+        overlay.addAlias(helper.createLayoutAlias(alias, LayoutAlias.GENERIC));
+        var overlays = [overlay, helper.createOverlay()];
+
+        return aliasWindow.createChartDiv({overlays: overlays});
+      }).then(function (div) {
+        assert.ok(div);
       });
-      aliasWindow.layoutAliases = [helper.createLayoutAlias(alias), null];
-      aliasWindow.layoutNames = ["x", "y"];
-      return aliasWindow.createChartDiv();
-    }).then(function (div) {
-      assert.ok(div);
     });
   });
 
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345325&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345325&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345325&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345325&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345330&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345330&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345330&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345330&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345330,345331,345337&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345330,345331,345337&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345330,345331,345337&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345330,345331,345337&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345331&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345331&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345331&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345331&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345334&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345334&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345334&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345334&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345337&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345337&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345337&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345337&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345339&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345339&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId&id=345339&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345339&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/drug_target_sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=436152&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/drug_target_sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=436152&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/drug_target_sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=436152&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/drug_target_sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=436152&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=-1&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=-1&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=-1&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=-1&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329157&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329157&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329157&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329157&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329159&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329159&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329159&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329159&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329163&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329163&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329163&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329163&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329167&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329167&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329167&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329167&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329168,329173&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329168,329173&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329168,329173&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329168,329173&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329170&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329170&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329170&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329170&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329171&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329171&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329171&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329171&token=MOCK_TOKEN_ID&
diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329173&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329173&token=MOCK_TOKEN_ID&
similarity index 100%
rename from frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId&id=329173&token=MOCK_TOKEN_ID&
rename to frontend-js/testFiles/apiCalls/projects/sample/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=329173&token=MOCK_TOKEN_ID&