From b8e37137b39c540347ab3ef6937958999e8153ba Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Fri, 20 Jul 2018 11:44:38 +0200
Subject: [PATCH] alias info window refacored - overlay data is obtained on
 demand by creating chart function

---
 .../src/main/js/map/AbstractCustomMap.js      |   21 -
 .../main/js/map/window/AbstractInfoWindow.js  | 1486 ++++++++---------
 .../src/main/js/map/window/AliasInfoWindow.js |  256 +--
 .../src/main/js/map/window/PointInfoWindow.js |  126 +-
 .../main/js/map/window/ReactionInfoWindow.js  |  528 +++---
 frontend-js/src/test/js/helper.js             |    7 +-
 frontend-js/src/test/js/map/CustomMap-test.js |   18 -
 .../js/map/window/AliasInfoWindow-test.js     |   29 +-
 8 files changed, 1225 insertions(+), 1246 deletions(-)

diff --git a/frontend-js/src/main/js/map/AbstractCustomMap.js b/frontend-js/src/main/js/map/AbstractCustomMap.js
index 68f84b6424..03f7119a22 100644
--- a/frontend-js/src/main/js/map/AbstractCustomMap.js
+++ b/frontend-js/src/main/js/map/AbstractCustomMap.js
@@ -571,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/window/AbstractInfoWindow.js b/frontend-js/src/main/js/map/window/AbstractInfoWindow.js
index c174f946a9..63a91ad290 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 0bc086e396..53ec9cda23 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,107 @@ 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) {
+        rowDiv.className = "mapChartRowEvenDiv";
+      } else {
+        rowDiv.className = "mapChartRowOddDiv";
+      }
+      rowDiv.style.position = "relative";
+      var nameDiv = document.createElement("div");
+      nameDiv.className = "mapChartNameDiv";
+      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);
           }
-
-        } 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();
-    }
-  }).then(function () {
+          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;
+      }
+    }));
+  });
+  return Promise.all(promises).then(function (rows) {
     for (var i = 0; i < rows.length; i++) {
       result.appendChild(rows[i]);
     }
@@ -215,18 +220,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 +246,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 +290,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 +346,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 +362,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 +375,7 @@ AliasInfoWindow.prototype.createGenomicDiv = function () {
               data: pileup.formats.vcf({
                 content: vcfContent
               }),
-              name: self.layoutNames[i] + ' - Variants',
+              name: overlays[i].getName() + ' - Variants',
               options: {
                 variantHeightByFrequency: true
               }
diff --git a/frontend-js/src/main/js/map/window/PointInfoWindow.js b/frontend-js/src/main/js/map/window/PointInfoWindow.js
index dc1c2d7f8b..bf263c8e7a 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 cca0f9d2c8..bd6b7c493c 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 30002e661c..11fc0efa18 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 353d80e088..880efabfaa 100644
--- a/frontend-js/src/test/js/map/CustomMap-test.js
+++ b/frontend-js/src/test/js/map/CustomMap-test.js
@@ -634,24 +634,6 @@ describe('CustomMap', function () {
     });
   });
 
-  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();
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 627d901f4e..c7dec871f2 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);
@@ -281,9 +281,9 @@ describe('AliasInfoWindow', function () {
       }).then(function () {
         var layoutAlias = helper.createLayoutAlias(alias);
         layoutAlias.setType(LayoutAlias.GENETIC_VARIANT);
-        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);
         win.destroy();
@@ -312,8 +312,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 +350,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);
@@ -392,9 +393,11 @@ describe('AliasInfoWindow', function () {
         map: map,
         marker: helper.createMarker({element: alias, map: map})
       });
-      aliasWindow.layoutAliases = [helper.createLayoutAlias(alias), null];
-      aliasWindow.layoutNames = ["x", "y"];
-      return aliasWindow.createChartDiv();
+      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);
     });
-- 
GitLab