diff --git a/.idea/modules.xml b/.idea/modules.xml
index 6e6dd9acbdd939bf1c110062e928a9ee7c27aa96..ae3706f24995e2278ace6b3dbfc2415ef574834a 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,28 @@
 <project version="4">
   <component name="ProjectModuleManager">
     <modules>
+      <module fileurl="file://$PROJECT_DIR$/CellDesigner-plugin/CellDesigner-plugin.iml" filepath="$PROJECT_DIR$/CellDesigner-plugin/CellDesigner-plugin.iml" />
+      <module fileurl="file://$PROJECT_DIR$/annotation/annotation.iml" filepath="$PROJECT_DIR$/annotation/annotation.iml" />
+      <module fileurl="file://$PROJECT_DIR$/commons/commons.iml" filepath="$PROJECT_DIR$/commons/commons.iml" />
+      <module fileurl="file://$PROJECT_DIR$/comparison/comparison.iml" filepath="$PROJECT_DIR$/comparison/comparison.iml" />
+      <module fileurl="file://$PROJECT_DIR$/console/console.iml" filepath="$PROJECT_DIR$/console/console.iml" />
+      <module fileurl="file://$PROJECT_DIR$/converter/converter.iml" filepath="$PROJECT_DIR$/converter/converter.iml" />
+      <module fileurl="file://$PROJECT_DIR$/converter-CellDesigner/converter-CellDesigner.iml" filepath="$PROJECT_DIR$/converter-CellDesigner/converter-CellDesigner.iml" />
+      <module fileurl="file://$PROJECT_DIR$/converter-SBGNML/converter-SBGNML.iml" filepath="$PROJECT_DIR$/converter-SBGNML/converter-SBGNML.iml" />
+      <module fileurl="file://$PROJECT_DIR$/converter-graphics/converter-graphics.iml" filepath="$PROJECT_DIR$/converter-graphics/converter-graphics.iml" />
+      <module fileurl="file://$PROJECT_DIR$/editor/editor.iml" filepath="$PROJECT_DIR$/editor/editor.iml" />
+      <module fileurl="file://$PROJECT_DIR$/frontend-js/frontend-js.iml" filepath="$PROJECT_DIR$/frontend-js/frontend-js.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/minerva.iml" filepath="$PROJECT_DIR$/.idea/minerva.iml" />
+      <module fileurl="file://$PROJECT_DIR$/model/model.iml" filepath="$PROJECT_DIR$/model/model.iml" />
+      <module fileurl="file://$PROJECT_DIR$/model-command/model-command.iml" filepath="$PROJECT_DIR$/model-command/model-command.iml" />
+      <module fileurl="file://$PROJECT_DIR$/parent.iml" filepath="$PROJECT_DIR$/parent.iml" />
+      <module fileurl="file://$PROJECT_DIR$/pathvisio/pathvisio.iml" filepath="$PROJECT_DIR$/pathvisio/pathvisio.iml" />
+      <module fileurl="file://$PROJECT_DIR$/persist/persist.iml" filepath="$PROJECT_DIR$/persist/persist.iml" />
+      <module fileurl="file://$PROJECT_DIR$/quadTrees/quadTrees.iml" filepath="$PROJECT_DIR$/quadTrees/quadTrees.iml" />
+      <module fileurl="file://$PROJECT_DIR$/reactome/reactome.iml" filepath="$PROJECT_DIR$/reactome/reactome.iml" />
+      <module fileurl="file://$PROJECT_DIR$/rest-api/rest-api.iml" filepath="$PROJECT_DIR$/rest-api/rest-api.iml" />
+      <module fileurl="file://$PROJECT_DIR$/service/service.iml" filepath="$PROJECT_DIR$/service/service.iml" />
+      <module fileurl="file://$PROJECT_DIR$/web/web.iml" filepath="$PROJECT_DIR$/web/web.iml" />
     </modules>
   </component>
 </project>
\ No newline at end of file
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java b/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
index 134f9ddb91de60742bb98ed094477bbca56fb842..b04589b96d9b7d64f661fa3fa45410365397d679 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
@@ -13,6 +13,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.zip.ZipFile;
 
 import javax.imageio.ImageIO;
 
@@ -90,17 +91,17 @@ public class OverviewParser {
 	private static final Integer BUFFER_SIZE															 = 1024;
 
 	/**
-	 * String identifing {@link OverviewModelLink} connections.
+	 * String identifying {@link OverviewModelLink} connections.
 	 */
 	private static final String	 MODEL_LINK_TYPE													 = "MODEL";
 
 	/**
-	 * String identifing {@link OverviewImageLink} connections between images.
+	 * String identifying {@link OverviewImageLink} connections between images.
 	 */
 	private static final String	 IMAGE_LINK_TYPE													 = "IMAGE";
 
 	/**
-	 * String identifing {@link OverviewSearchLink} connections.
+	 * String identifying {@link OverviewSearchLink} connections.
 	 */
 	private static final String	 SEARCH_LINK_TYPE													 = "SEARCH";
 
@@ -125,7 +126,7 @@ public class OverviewParser {
 	 * @throws InvalidOverviewFile
 	 *           thrown when the zip file contains invalid data
 	 */
-	public List<OverviewImage> parseOverviewLinks(Set<Model> models, List<ImageZipEntryFile> files, String outputDirectory) throws InvalidOverviewFile {
+	public List<OverviewImage> parseOverviewLinks(Set<Model> models, List<ImageZipEntryFile> files, String outputDirectory, ZipFile zipFile) throws InvalidOverviewFile {
 		if (outputDirectory != null) {
 			File f = new File(outputDirectory);
 			if (!f.exists()) {
@@ -151,14 +152,14 @@ public class OverviewParser {
 					// copy file to file system
 					if (outputDirectory != null) {
 						imageFile = new File(outputDirectory + "/" + filename);
-					} else { // or temp file
+					} else { // or temporary file
 						imageFile = File.createTempFile("temp-file-name", ".png");
 						imageFile.deleteOnExit();
 					}
 					FileOutputStream fos = new FileOutputStream(imageFile);
 					byte[] bytes = new byte[BUFFER_SIZE];
 					int length;
-					InputStream is = entry.getInputStream();
+					InputStream is = zipFile.getInputStream(zipFile.getEntry(entry.getFilename()));
 					while ((length = is.read(bytes)) >= 0) {
 						fos.write(bytes, 0, length);
 					}
@@ -175,7 +176,7 @@ public class OverviewParser {
 					StringBuilder sb = new StringBuilder("");
 					byte[] buffer = new byte[BUFFER_SIZE];
 					int read = 0;
-					InputStream is = entry.getInputStream();
+                    InputStream is = zipFile.getInputStream(zipFile.getEntry(entry.getFilename()));
 					while ((read = is.read(buffer)) >= 0) {
 						sb.append(new String(buffer, 0, read));
 					}
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
index 11c34bf4ef262a1ceda2b7d412082c57e99aac5c..357a3da36158df62a51e1db9fc4d6a05f5980526 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
@@ -58,7 +58,7 @@ public class ProjectFactory {
 
 		if (imageEntries.size() > 0) {
 			OverviewParser parser = new OverviewParser();
-			project.addOverviewImages(parser.parseOverviewLinks(models, imageEntries, params.getVisualizationDir()));
+			project.addOverviewImages(parser.parseOverviewLinks(models, imageEntries, params.getVisualizationDir(), zipFile));
 		}
 		return project;
 	}
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/zip/ImageZipEntryFile.java b/converter/src/main/java/lcsb/mapviewer/converter/zip/ImageZipEntryFile.java
index 5b930d5dfb2237bb2320da91643ae352f25757c1..cef2d1956bf09943b4b837f36245625cc1abc5a6 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/zip/ImageZipEntryFile.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/zip/ImageZipEntryFile.java
@@ -1,9 +1,6 @@
 package lcsb.mapviewer.converter.zip;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.Serializable;
 
 /**
@@ -15,67 +12,31 @@ import java.io.Serializable;
  */
 public class ImageZipEntryFile extends ZipEntryFile implements Serializable {
 
-	/**
-	 * 
-	 */
-	private static final long	serialVersionUID	= 1L;
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Default constructor.
+   */
+  public ImageZipEntryFile() {
+
+  }
+
+  /**
+   * Default constructor.
+   * 
+   * @param filename
+   *          {@link ZipEntryFile#filename}
+   * @param inputStream
+   *          input stream with the data for this entry.
+   * @see #baos
+   * @throws IOException
+   *           thrown when there is a problem with accessing input stream
+   */
+  public ImageZipEntryFile(String filename) {
+    super(filename);
+  }
 
-	/**
-	 * Size of the buffer used to copy input streams.
-	 */
-	private static final int	BUFFER_SIZE				= 1024;
-
-	/**
-	 * Copy of the {@link InputStream} of the file.
-	 */
-	private byte[]						bytes;
-
-	/**
-	 * Default constructor.
-	 */
-	public ImageZipEntryFile() {
-
-	}
-
-	/**
-	 * Default constructor.
-	 * 
-	 * @param filename
-	 *          {@link ZipEntryFile#filename}
-	 * @param inputStream
-	 *          input stream with the data for this entry.
-	 * @see #baos
-	 * @throws IOException
-	 *           thrown when there is a problem with accessing input stream
-	 */
-	public ImageZipEntryFile(String filename, InputStream inputStream) throws IOException {
-		super(filename);
-		setInputStream(inputStream);
-	}
-
-	/**
-	 * @return the inputStream
-	 * @see #inputStream
-	 */
-	public InputStream getInputStream() {
-		return new ByteArrayInputStream(bytes);
-	}
-
-	/**
-	 * @param inputStream
-	 *          the inputStream to set
-	 * @throws IOException
-	 *           thrown when there is aproblem with accessing inputStream
-	 * @see #inputStream
-	 */
-	public void setInputStream(InputStream inputStream) throws IOException {
-		ByteArrayOutputStream baos = new ByteArrayOutputStream();
-		byte[] buffer = new byte[BUFFER_SIZE];
-		int len;
-		while ((len = inputStream.read(buffer)) > -1) {
-			baos.write(buffer, 0, len);
-		}
-		baos.flush();
-		bytes = baos.toByteArray();
-	}
 }
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/zip/LayoutZipEntryFile.java b/converter/src/main/java/lcsb/mapviewer/converter/zip/LayoutZipEntryFile.java
index 164b0aeba75d212a4f09c981faa4954e0d4b34a4..99b30c9a5e02cc81a1d4598c8c52a31a378b1aa5 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/zip/LayoutZipEntryFile.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/zip/LayoutZipEntryFile.java
@@ -11,70 +11,72 @@ import java.io.Serializable;
  */
 public class LayoutZipEntryFile extends ZipEntryFile implements Serializable {
 
-	/**
-	 * 
-	 */
-	private static final long serialVersionUID = 1L;
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1L;
 
-	/**
-	 * Name of the layout.
-	 */
-	private String						name						 = "";
+  /**
+   * Name of the layout.
+   */
+  private String name = "";
 
-	/**
-	 * Description of the layout.
-	 */
-	private String						description			= "";
+  /**
+   * Description of the layout.
+   */
+  private String description = "";
 
-	/**
-	 * Default constructor.
-	 */
-	public LayoutZipEntryFile() {
+  /**
+   * Default constructor.
+   */
+  public LayoutZipEntryFile() {
 
-	}
+  }
 
-	/**
-	 * Default constructor.
-	 * 
-	 * @param filename
-	 *          {@link ZipEntryFile#filename}
-	 */
-	public LayoutZipEntryFile(String filename) {
-		super(filename);
-	}
+  /**
+   * Default constructor.
+   * 
+   * @param filename
+   *          {@link ZipEntryFile#filename}
+   */
+  public LayoutZipEntryFile(String filename, String name, String description) {
+    super(filename);
+    this.name = name;
+    this.description = description;
+  }
 
-	/**
-	 * @return the name
-	 * @see #name
-	 */
-	public String getName() {
-		return name;
-	}
+  /**
+   * @return the name
+   * @see #name
+   */
+  public String getName() {
+    return name;
+  }
 
-	/**
-	 * @param name
-	 *          the name to set
-	 * @see #name
-	 */
-	public void setName(String name) {
-		this.name = name;
-	}
+  /**
+   * @param name
+   *          the name to set
+   * @see #name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
 
-	/**
-	 * @return the description
-	 * @see #description
-	 */
-	public String getDescription() {
-		return description;
-	}
+  /**
+   * @return the description
+   * @see #description
+   */
+  public String getDescription() {
+    return description;
+  }
 
-	/**
-	 * @param description
-	 *          the description to set
-	 * @see #description
-	 */
-	public void setDescription(String description) {
-		this.description = description;
-	}
+  /**
+   * @param description
+   *          the description to set
+   * @see #description
+   */
+  public void setDescription(String description) {
+    this.description = description;
+  }
 
 }
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
index 084e575cd01cac40a83cbe5d336b070865b62328..1bcdc4b20e5ebcf96f9b4efce7de1af63a80a909 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
@@ -165,7 +165,7 @@ public class ZipEntryFileFactory {
 				}
 				return zesf;
 			} else if (directory.equals(IMAGES_DIRECTORY)) {
-				ImageZipEntryFile result = new ImageZipEntryFile(entry.getName(), zipFile.getInputStream(entry));
+				ImageZipEntryFile result = new ImageZipEntryFile(entry.getName());
 				return result;
 			} else if (directory.equals(LAYOUT_DIRECTORY)) {
 				LayoutZipEntryFile result = createLayoutZipEntryFile(entry.getName(), zipFile.getInputStream(entry));
diff --git a/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java b/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
index 50777ea476c2b48bf91f150162df8119b0e9cb58..feace015aecc51cdc5e9432d02f5f178aa45b73d 100644
--- a/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
+++ b/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
@@ -10,9 +10,12 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.log4j.Logger;
@@ -28,226 +31,237 @@ import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelFullIndexed;
 
 public class OverviewParserTest {
-	Logger				 logger	= Logger.getLogger(OverviewParserTest.class);
-	OverviewParser parser	= new OverviewParser();
-
-	@Before
-	public void setUp() throws Exception {
-	}
-
-	@After
-	public void tearDown() throws Exception {
-	}
-
-	@Test
-	public void testParsingValidFile() throws Exception {
-		try {
-			Set<Model> models = createValidTestMapModel();
-			List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/valid_overview");
-			List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, null);
-			assertNotNull(result);
-			assertEquals(1, result.size());
-
-			OverviewImage img = result.get(0);
-
-			assertEquals("test.png", img.getFilename());
-			assertEquals((Integer) 639, img.getHeight());
-			assertEquals((Integer) 963, img.getWidth());
-			assertEquals(2, img.getLinks().size());
-
-			OverviewLink link = img.getLinks().get(0);
-			List<Point2D> polygon = link.getPolygonCoordinates();
-			assertEquals(4, polygon.size());
-
-			assertTrue(link instanceof OverviewModelLink);
-
-			OverviewModelLink mLink = (OverviewModelLink) link;
-			Model mainModel = models.iterator().next();
-			assertEquals(mainModel.getModelData(), mLink.getLinkedModel());
-			assertEquals((Integer) 10, mLink.getxCoord());
-			assertEquals((Integer) 10, mLink.getyCoord());
-			assertEquals((Integer) 3, mLink.getZoomLevel());
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	private List<ImageZipEntryFile> createImageEntries(String string) throws IOException {
-		List<ImageZipEntryFile> result = new ArrayList<ImageZipEntryFile>();
-		for (final File fileEntry : new File(string).listFiles()) {
-			if (!fileEntry.isDirectory()) {
-				result.add(new ImageZipEntryFile(fileEntry.getName(), new FileInputStream(fileEntry)));
-			}
-		}
-		return result;
-	}
-
-	@Test
-	public void testParsingValidFile2() throws Exception {
-		try {
-			Set<Model> models = createValidTestMapModel();
-
-			String tmpDir = "tmp";
-
-			new File(tmpDir).mkdirs();
-			List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/valid_overview");
-			List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, tmpDir);
-
-			assertTrue(new File(tmpDir + "/test.png").exists());
-
-			assertNotNull(result);
-			assertEquals(1, result.size());
-			OverviewImage img = result.get(0);
-			assertEquals("test.png", img.getFilename());
-
-			new File(tmpDir).delete();
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParsingInvalidFile1() throws Exception {
-		try {
-			List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_1");
-			Set<Model> models = createValidTestMapModel();
-
-			parser.parseOverviewLinks(models, imageEntries, null);
-			fail("Exception expected");
-		} catch (InvalidOverviewFile e) {
-			assertTrue(e.getMessage().contains("Unknown image filename"));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParsingInvalidFile2() throws Exception {
-		try {
-			List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_2");
-			Set<Model> models = createValidTestMapModel();
-
-			parser.parseOverviewLinks(models, imageEntries, null);
-			fail("Exception expected");
-		} catch (InvalidOverviewFile e) {
-			assertTrue(e.getMessage().contains("Unknown model"));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParsingInvalidFile3() throws Exception {
-		try {
-			List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_3");
-			Set<Model> models = createValidTestMapModel();
-
-			parser.parseOverviewLinks(models, imageEntries, null);
-			fail("Exception expected");
-		} catch (InvalidOverviewFile e) {
-			assertTrue(e.getMessage().contains("coordinates outside image"));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	private Set<Model> createValidTestMapModel() {
-		Set<Model> result = new HashSet<>();
-		Model model = new ModelFullIndexed(null);
-		model.setName("main");
-		result.add(model);
-		return result;
-	}
-
-	/**
-	 * Test coordinates that overlap (exception is expected).
-	 * 
-	 * @throws Exception
-	 */
-	@Test
-	public void testParseInvalidCoordinates() throws Exception {
-		try {
-			String invalidCoordinates = "test.png	10,10 100,10 100,100 10,10	main.xml	10,10	3\n" + //
-					"test.png	10,10 10,400 400,400 400,10	main.xml	10,10	4";
-			Set<Model> models = createValidTestMapModel();
-
-			List<OverviewImage> images = new ArrayList<OverviewImage>();
-			OverviewImage oi = new OverviewImage();
-			oi.setFilename("test.png");
-			oi.setWidth(1000);
-			oi.setHeight(1000);
-			images.add(oi);
-
-			parser.processCoordinates(models, images, invalidCoordinates);
-
-			fail("Exception expected");
-		} catch (InvalidOverviewFile e) {
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParseValidCoordinates() throws Exception {
-		try {
-			String invalidCoordinates = "FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE\n" + //
-					"test.png	10,10 100,10 100,100 10,10	main.xml	10,10	3	MODEL\n" + //
-					"test.png	200,200 200,400 400,400 400,200	main.xml	10,10	4	MODEL";
-			Set<Model> models = createValidTestMapModel();
-
-			List<OverviewImage> images = new ArrayList<OverviewImage>();
-			OverviewImage oi = new OverviewImage();
-			oi.setFilename("test.png");
-			oi.setWidth(1000);
-			oi.setHeight(1000);
-			images.add(oi);
-
-			parser.processCoordinates(models, images, invalidCoordinates);
-
-			assertEquals(2, oi.getLinks().size());
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParseValidComplexCoordinates() throws Exception {
-		try {
-			String invalidCoordinates = FileUtils.readFileToString(new File("testFiles/coordinates.txt"));
-			Set<Model> models = createValidTestMapModel();
-
-			List<OverviewImage> images = new ArrayList<>();
-			OverviewImage oi = new OverviewImage();
-			oi.setFilename("test.png");
-			oi.setWidth(1000);
-			oi.setHeight(1000);
-			images.add(oi);
-
-			OverviewImage oi2 = new OverviewImage();
-			oi2.setFilename("test2.png");
-			oi2.setWidth(1000);
-			oi2.setHeight(1000);
-			images.add(oi2);
-
-			parser.processCoordinates(models, images, invalidCoordinates);
-
-			assertEquals(2, oi.getLinks().size());
-			assertEquals(1, oi2.getLinks().size());
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
+  private static final String TEST_FILES_VALID_OVERVIEW_ZIP = "testFiles/valid_overview.zip";
+  Logger logger = Logger.getLogger(OverviewParserTest.class);
+  OverviewParser parser = new OverviewParser();
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void testParsingValidFile() throws Exception {
+    try {
+      Set<Model> models = createValidTestMapModel();
+      List<ImageZipEntryFile> imageEntries = createImageEntries(TEST_FILES_VALID_OVERVIEW_ZIP);
+      List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, null,
+          new ZipFile(TEST_FILES_VALID_OVERVIEW_ZIP));
+      assertNotNull(result);
+      assertEquals(1, result.size());
+
+      OverviewImage img = result.get(0);
+
+      assertEquals("test.png", img.getFilename());
+      assertEquals((Integer) 639, img.getHeight());
+      assertEquals((Integer) 963, img.getWidth());
+      assertEquals(2, img.getLinks().size());
+
+      OverviewLink link = img.getLinks().get(0);
+      List<Point2D> polygon = link.getPolygonCoordinates();
+      assertEquals(4, polygon.size());
+
+      assertTrue(link instanceof OverviewModelLink);
+
+      OverviewModelLink mLink = (OverviewModelLink) link;
+      Model mainModel = models.iterator().next();
+      assertEquals(mainModel.getModelData(), mLink.getLinkedModel());
+      assertEquals((Integer) 10, mLink.getxCoord());
+      assertEquals((Integer) 10, mLink.getyCoord());
+      assertEquals((Integer) 3, mLink.getZoomLevel());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  private List<ImageZipEntryFile> createImageEntries(String string) throws IOException {
+    List<ImageZipEntryFile> result = new ArrayList<>();
+
+    ZipFile zipFile = new ZipFile(string);
+    try {
+      Enumeration<? extends ZipEntry> entries = zipFile.entries();
+      while (entries.hasMoreElements()) {
+        ZipEntry entry = entries.nextElement();
+        if (!entry.isDirectory()) {
+          result.add(new ImageZipEntryFile(entry.getName()));
+        }
+      }
+      return result;
+    } finally {
+      zipFile.close();
+    }
+  }
+
+  @Test
+  public void testParsingValidFile2() throws Exception {
+    try {
+      Set<Model> models = createValidTestMapModel();
+
+      String tmpDir = "tmp";
+
+      new File(tmpDir).mkdirs();
+      List<ImageZipEntryFile> imageEntries = createImageEntries(TEST_FILES_VALID_OVERVIEW_ZIP);
+      List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, tmpDir,
+          new ZipFile(TEST_FILES_VALID_OVERVIEW_ZIP));
+
+      assertTrue(new File(tmpDir + "/test.png").exists());
+
+      assertNotNull(result);
+      assertEquals(1, result.size());
+      OverviewImage img = result.get(0);
+      assertEquals("test.png", img.getFilename());
+
+      new File(tmpDir).delete();
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParsingInvalidFile1() throws Exception {
+    try {
+      List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_1.zip");
+      Set<Model> models = createValidTestMapModel();
+
+      parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_1.zip"));
+      fail("Exception expected");
+    } catch (InvalidOverviewFile e) {
+      assertTrue(e.getMessage().contains("Unknown image filename"));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParsingInvalidFile2() throws Exception {
+    try {
+      List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_2.zip");
+      Set<Model> models = createValidTestMapModel();
+
+      parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_2.zip"));
+      fail("Exception expected");
+    } catch (InvalidOverviewFile e) {
+      assertTrue(e.getMessage().contains("Unknown model"));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParsingInvalidFile3() throws Exception {
+    try {
+      List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_3.zip");
+      Set<Model> models = createValidTestMapModel();
+
+      parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_3.zip"));
+      fail("Exception expected");
+    } catch (InvalidOverviewFile e) {
+      assertTrue(e.getMessage().contains("coordinates outside image"));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  private Set<Model> createValidTestMapModel() {
+    Set<Model> result = new HashSet<>();
+    Model model = new ModelFullIndexed(null);
+    model.setName("main");
+    result.add(model);
+    return result;
+  }
+
+  /**
+   * Test coordinates that overlap (exception is expected).
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testParseInvalidCoordinates() throws Exception {
+    try {
+      String invalidCoordinates = "test.png	10,10 100,10 100,100 10,10	main.xml	10,10	3\n" + //
+          "test.png	10,10 10,400 400,400 400,10	main.xml	10,10	4";
+      Set<Model> models = createValidTestMapModel();
+
+      List<OverviewImage> images = new ArrayList<OverviewImage>();
+      OverviewImage oi = new OverviewImage();
+      oi.setFilename("test.png");
+      oi.setWidth(1000);
+      oi.setHeight(1000);
+      images.add(oi);
+
+      parser.processCoordinates(models, images, invalidCoordinates);
+
+      fail("Exception expected");
+    } catch (InvalidOverviewFile e) {
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseValidCoordinates() throws Exception {
+    try {
+      String invalidCoordinates = "FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE\n" + //
+          "test.png	10,10 100,10 100,100 10,10	main.xml	10,10	3	MODEL\n" + //
+          "test.png	200,200 200,400 400,400 400,200	main.xml	10,10	4	MODEL";
+      Set<Model> models = createValidTestMapModel();
+
+      List<OverviewImage> images = new ArrayList<OverviewImage>();
+      OverviewImage oi = new OverviewImage();
+      oi.setFilename("test.png");
+      oi.setWidth(1000);
+      oi.setHeight(1000);
+      images.add(oi);
+
+      parser.processCoordinates(models, images, invalidCoordinates);
+
+      assertEquals(2, oi.getLinks().size());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseValidComplexCoordinates() throws Exception {
+    try {
+      String invalidCoordinates = FileUtils.readFileToString(new File("testFiles/coordinates.txt"));
+      Set<Model> models = createValidTestMapModel();
+
+      List<OverviewImage> images = new ArrayList<>();
+      OverviewImage oi = new OverviewImage();
+      oi.setFilename("test.png");
+      oi.setWidth(1000);
+      oi.setHeight(1000);
+      images.add(oi);
+
+      OverviewImage oi2 = new OverviewImage();
+      oi2.setFilename("test2.png");
+      oi2.setWidth(1000);
+      oi2.setHeight(1000);
+      images.add(oi2);
+
+      parser.processCoordinates(models, images, invalidCoordinates);
+
+      assertEquals(2, oi.getLinks().size());
+      assertEquals(1, oi2.getLinks().size());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
 
 }
diff --git a/converter/src/test/resources/log4j.properties b/converter/src/test/resources/log4j.properties
index ec610a35af6964418be577ea1bce3ed615e8f91e..4d46bcbdbbff98b6f0ab577993b51da5a200c6d3 100644
--- a/converter/src/test/resources/log4j.properties
+++ b/converter/src/test/resources/log4j.properties
@@ -1,5 +1,5 @@
 #Set root logger 's level and its appender to an appender called CONSOLE which is defined below.
-log4j.rootLogger=info, CONSOLE, R
+log4j.rootLogger=info, CONSOLE
 
 #Set the behavior of the CONSOLE appender 
 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
diff --git a/converter/testFiles/invalid_overview_1.zip b/converter/testFiles/invalid_overview_1.zip
new file mode 100644
index 0000000000000000000000000000000000000000..b3040204788c875114301808c2920fee4dda812b
Binary files /dev/null and b/converter/testFiles/invalid_overview_1.zip differ
diff --git a/converter/testFiles/invalid_overview_1/coords.txt b/converter/testFiles/invalid_overview_1/coords.txt
deleted file mode 100644
index 6b604ee16c9737e8fce2be69ca0064314a1a141c..0000000000000000000000000000000000000000
--- a/converter/testFiles/invalid_overview_1/coords.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE
-unknowntest.png	10,10 100,10 100,100 100,10	main.xml	10,10	3	MODEL
-test.png	200,200 200,400 400,400 400,200	main.xml	1000,1000	4	MODEL
-
diff --git a/converter/testFiles/invalid_overview_1/test.png b/converter/testFiles/invalid_overview_1/test.png
deleted file mode 100644
index adbe702689a43364dc6f0a6cebedcc9277388a9c..0000000000000000000000000000000000000000
Binary files a/converter/testFiles/invalid_overview_1/test.png and /dev/null differ
diff --git a/converter/testFiles/invalid_overview_2.zip b/converter/testFiles/invalid_overview_2.zip
new file mode 100644
index 0000000000000000000000000000000000000000..c2d52c9ce246a4df4b3563863c1ca122927c7958
Binary files /dev/null and b/converter/testFiles/invalid_overview_2.zip differ
diff --git a/converter/testFiles/invalid_overview_2/coords.txt b/converter/testFiles/invalid_overview_2/coords.txt
deleted file mode 100644
index d25072502f67096da21cace09230e9eb01e87cc5..0000000000000000000000000000000000000000
--- a/converter/testFiles/invalid_overview_2/coords.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE
-test.png	10,10 100,10 100,100 100,10	unknown_main.xml	10,10	3	MODEL
-test.png	200,200 200,400 400,400 400,200	main.xml	1000,1000	4	MODEL
-
diff --git a/converter/testFiles/invalid_overview_2/test.png b/converter/testFiles/invalid_overview_2/test.png
deleted file mode 100644
index adbe702689a43364dc6f0a6cebedcc9277388a9c..0000000000000000000000000000000000000000
Binary files a/converter/testFiles/invalid_overview_2/test.png and /dev/null differ
diff --git a/converter/testFiles/invalid_overview_3.zip b/converter/testFiles/invalid_overview_3.zip
new file mode 100644
index 0000000000000000000000000000000000000000..9b995cf8be6b2a267e7f4f440bfce870ecbf761a
Binary files /dev/null and b/converter/testFiles/invalid_overview_3.zip differ
diff --git a/converter/testFiles/invalid_overview_3/coords.txt b/converter/testFiles/invalid_overview_3/coords.txt
deleted file mode 100644
index 63839f6e7d91535a62b50690b3ca1d662ac65dcf..0000000000000000000000000000000000000000
--- a/converter/testFiles/invalid_overview_3/coords.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE
-test.png	10,10 100,10 100,100 10000,10	main.xml	10,10	3	MODEL
-test.png	200,200 200,400 400,400 400,200	main.xml	1000,1000	4	MODEL
-
diff --git a/converter/testFiles/invalid_overview_3/test.png b/converter/testFiles/invalid_overview_3/test.png
deleted file mode 100644
index adbe702689a43364dc6f0a6cebedcc9277388a9c..0000000000000000000000000000000000000000
Binary files a/converter/testFiles/invalid_overview_3/test.png and /dev/null differ
diff --git a/converter/testFiles/valid_overview.zip b/converter/testFiles/valid_overview.zip
new file mode 100644
index 0000000000000000000000000000000000000000..edfff4deed85439da9ae079ad6a22be298f9a42a
Binary files /dev/null and b/converter/testFiles/valid_overview.zip differ
diff --git a/converter/testFiles/valid_overview/coords.txt b/converter/testFiles/valid_overview/coords.txt
deleted file mode 100644
index 6cc5a4478fdef368e2c1e9e1f21e54fc9794e308..0000000000000000000000000000000000000000
--- a/converter/testFiles/valid_overview/coords.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-FILE	POLYGON	LINK_TARGET	MODEL_COORDINATES	MODEL_ZOOM_LEVEL	LINK_TYPE
-test.png	10,10 100,10 100,100 100,10	main.xml	10,10	3	MODEL
-test.png	200,200 200,400 400,400 400,200	main.xml	1000,1000	4	MODEL
-
diff --git a/converter/testFiles/valid_overview/test.png b/converter/testFiles/valid_overview/test.png
deleted file mode 100644
index adbe702689a43364dc6f0a6cebedcc9277388a9c..0000000000000000000000000000000000000000
Binary files a/converter/testFiles/valid_overview/test.png and /dev/null differ
diff --git a/frontend-js/.idea/frontend-js.iml b/frontend-js/.idea/frontend-js.iml
index 4feb4987b6dd589318a78302c0367c39c36535a9..217608cacb6a1c303a5e7e7c1b1ad8b1338de378 100644
--- a/frontend-js/.idea/frontend-js.iml
+++ b/frontend-js/.idea/frontend-js.iml
@@ -4,11 +4,12 @@
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/.tmp" />
-      <excludeFolder url="file://$MODULE_DIR$/temp" />
       <excludeFolder url="file://$MODULE_DIR$/dist" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
       <excludeFolder url="file://$MODULE_DIR$/tmp" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="chai-DefinitelyTyped" level="application" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/frontend-js/package.json b/frontend-js/package.json
index dbf87572a8bb5b7aac10a6b456fcb358ff06198e..6608366a592f90159e988c0da56c07e56cc6ad30 100644
--- a/frontend-js/package.json
+++ b/frontend-js/package.json
@@ -43,6 +43,7 @@
     "http-status-codes": "^1.3.0",
     "js-cookie": "^2.1.3",
     "jstree": "^3.3.4",
+    "jszip": "^3.1.4",
     "log4js": "0.6.38",
     "pileup": "^0.6.8",
     "request": "^2.82.0"
diff --git a/frontend-js/src/main/js/Configuration.js b/frontend-js/src/main/js/Configuration.js
index f8bf26513b79f892ac70104ce220e94ed300fe0c..5dc187dd7cf1af381eddfbba546837ad98233358 100644
--- a/frontend-js/src/main/js/Configuration.js
+++ b/frontend-js/src/main/js/Configuration.js
@@ -49,6 +49,7 @@ function Configuration(json) {
   self.setElementTypes(json.elementTypes);
   self.setReactionTypes(json.reactionTypes);
   self.setMiriamTypes(json.miriamTypes);
+  self.setMapTypes(json.mapTypes);
   self.setModificationStateTypes(json.modificationStateTypes);
   self.setPrivilegeTypes(json.privilegeTypes);
   self.setAnnotators(json.annotators);
@@ -98,12 +99,12 @@ Configuration.prototype.getElementTypeNames = function () {
 
 Configuration.prototype.getParentType = function (elementType) {
   var i;
-  for (var i = 0; i < this._elementTypes.length; i++) {
+  for (i = 0; i < this._elementTypes.length; i++) {
     if (this._elementTypes[i].className === elementType.parentClass) {
       return this._elementTypes[i];
     }
   }
-  for (var i = 0; i < this._reactionTypes.length; i++) {
+  for (i = 0; i < this._reactionTypes.length; i++) {
     if (this._reactionTypes[i].className === elementType.parentClass) {
       return this._reactionTypes[i];
     }
@@ -152,6 +153,13 @@ Configuration.prototype.setMiriamTypes = function (miriamTypes) {
   );
 };
 
+Configuration.prototype.setMapTypes = function (mapTypes) {
+  this._mapTypes = mapTypes;
+};
+Configuration.prototype.getMapTypes = function () {
+  return this._mapTypes;
+};
+
 Configuration.prototype.setPrivilegeTypes = function (privilegeTypes) {
   this._privilegeTypes = [];
   for (var key in privilegeTypes) {
@@ -206,7 +214,7 @@ Configuration.prototype.setAnnotators = function (annotators) {
   for (var key in annotators) {
     if (annotators.hasOwnProperty(key)) {
       var annotator = annotators[key];
-      this._annotators.push(new Annotator(annotators[key], this));
+      this._annotators.push(new Annotator(annotator, this));
     }
   }
 };
diff --git a/frontend-js/src/main/js/gui/AddOverlayDialog.js b/frontend-js/src/main/js/gui/AddOverlayDialog.js
index b78dc728ab4ed3fc4cb752f5e0a19e7e646f5394..857b2930e528de8b0b724b91be4ce5276d0d0a70 100644
--- a/frontend-js/src/main/js/gui/AddOverlayDialog.js
+++ b/frontend-js/src/main/js/gui/AddOverlayDialog.js
@@ -8,6 +8,8 @@ var GuiUtils = require('./leftPanel/GuiUtils');
 var LayoutData = require('../map/data/LayoutData');
 var NetworkError = require('../NetworkError');
 
+var OverlayParser = require('../map/OverlayParser');
+
 var Functions = require('../Functions');
 var logger = require('../logger');
 var HttpStatus = require('http-status-codes');
@@ -24,7 +26,7 @@ function AddOverlayDialog(params) {
 AddOverlayDialog.prototype = Object.create(AbstractGuiElement.prototype);
 AddOverlayDialog.prototype.constructor = AddOverlayDialog;
 
-AddOverlayDialog.prototype.createGui = function() {
+AddOverlayDialog.prototype.createGui = function () {
   var self = this;
   var guiUtils = new GuiUtils();
   var content = document.createElement("div");
@@ -32,9 +34,9 @@ AddOverlayDialog.prototype.createGui = function() {
   content.style.height = "100%";
   content.appendChild(guiUtils.createLabel("Name: "));
   var nameInput = Functions.createElement({
-    type : "input",
-    inputType : "text",
-    name : "overlay-name",
+    type: "input",
+    inputType: "text",
+    name: "overlay-name",
   });
   content.appendChild(nameInput);
   content.appendChild(guiUtils.createNewLine());
@@ -42,19 +44,19 @@ AddOverlayDialog.prototype.createGui = function() {
   content.appendChild(guiUtils.createLabel("Description: "));
   content.appendChild(guiUtils.createNewLine());
   var descriptionInput = Functions.createElement({
-    type : "textarea",
-    name : "overlay-description",
+    type: "textarea",
+    name: "overlay-description",
   });
   content.appendChild(descriptionInput);
   content.appendChild(guiUtils.createNewLine());
 
   content.appendChild(guiUtils.createLabel("Upload file: "));
   var fileInput = Functions.createElement({
-    type : "input",
-    inputType : "file",
-    name : "overlay-file",
+    type: "input",
+    inputType: "file",
+    name: "overlay-file",
   });
-  fileInput.addEventListener("change", function() {
+  fileInput.addEventListener("change", function () {
     return self.processFile(fileInput.files[0]);
   }, false);
   content.appendChild(fileInput);
@@ -63,8 +65,8 @@ AddOverlayDialog.prototype.createGui = function() {
   content.appendChild(guiUtils.createLabel("Or provide list of elements here (one per line): "));
   content.appendChild(guiUtils.createNewLine());
   var contentInput = Functions.createElement({
-    type : "textarea",
-    name : "overlay-content",
+    type: "textarea",
+    name: "overlay-content",
   });
   content.appendChild(contentInput);
   content.appendChild(guiUtils.createNewLine());
@@ -72,21 +74,22 @@ AddOverlayDialog.prototype.createGui = function() {
   self.getElement().appendChild(content);
 };
 
-AddOverlayDialog.prototype.processFile = function(file) {
+AddOverlayDialog.prototype.processFile = function (file) {
   var self = this;
   self.setFileContent(null);
   if (file) {
-    return new Promise(function(resolve, reject) {
+    return new Promise(function (resolve, reject) {
       var reader = new FileReader();
       reader.readAsText(file, "UTF-8");
-      reader.onload = function(evt) {
+      reader.onload = function (evt) {
         try {
+          var overlayParser = new OverlayParser();
           self.setFileContent(evt.target.result);
-          var data = self.parseFile(evt.target.result);
+          var overlay = overlayParser.parse(evt.target.result);
           var nameInput = $("[name='overlay-name']", self.getElement())[0];
           var descriptionInput = $("[name='overlay-description']", self.getElement())[0];
-          if (data.name !== undefined) {
-            nameInput.value = data.name;
+          if (overlay.getName() !== undefined) {
+            nameInput.value = overlay.getName();
           } else {
             var filename = $("[name='overlay-file']", self.getElement())[0].value;
             if (filename.indexOf(".") > 0) {
@@ -97,15 +100,15 @@ AddOverlayDialog.prototype.processFile = function(file) {
             }
             nameInput.value = filename;
           }
-          if (data.description !== undefined) {
-            descriptionInput.value = data.description;
+          if (overlay.getDescription() !== undefined) {
+            descriptionInput.value = overlay.getDescription();
           }
           resolve(self.getFileContent());
         } catch (error) {
           reject(error);
         }
       };
-      reader.onerror = function() {
+      reader.onerror = function () {
         reject(new Error("Problem reading file"));
       };
     });
@@ -114,11 +117,11 @@ AddOverlayDialog.prototype.processFile = function(file) {
   }
 };
 
-AddOverlayDialog.prototype.setFileContent = function(fileContent) {
+AddOverlayDialog.prototype.setFileContent = function (fileContent) {
   this._fileContent = fileContent;
 };
 
-AddOverlayDialog.prototype.getFileContent = function() {
+AddOverlayDialog.prototype.getFileContent = function () {
   var self = this;
   var contentInput = $("[name='overlay-content']", self.getElement())[0];
 
@@ -135,43 +138,43 @@ AddOverlayDialog.prototype.getFileContent = function() {
   }
 };
 
-AddOverlayDialog.prototype.init = function() {
+AddOverlayDialog.prototype.init = function () {
   return Promise.resolve();
 };
 
-AddOverlayDialog.prototype.addOverlay = function() {
+AddOverlayDialog.prototype.addOverlay = function () {
   var self = this;
   var nameInput = $("[name='overlay-name']", self.getElement())[0];
   var descriptionInput = $("[name='overlay-description']", self.getElement())[0];
   var filename = $("[name='overlay-file']", self.getElement())[0].value;
   var overlay = new LayoutData({
-    name : nameInput.value,
-    description : descriptionInput.value,
-    content : self.getFileContent(),
-    filename : filename,
+    name: nameInput.value,
+    description: descriptionInput.value,
+    content: self.getFileContent(),
+    filename: filename,
   });
   GuiConnector.showProcessing();
   return ServerConnector.addOverlay({
-    overlay : overlay,
-    projectId : self.getProject().getProjectId(),
-  }).then(function(result) {
+    overlay: overlay,
+    projectId: self.getProject().getProjectId(),
+  }).then(function (result) {
     overlay = result;
     GuiConnector.hideProcessing();
     return self.callListeners("onAddOverlay", overlay);
   });
 };
 
-AddOverlayDialog.prototype.destroy = function() {
+AddOverlayDialog.prototype.destroy = function () {
   $(this.getElement()).dialog("destroy");
 };
 
-AddOverlayDialog.prototype.open = function() {
+AddOverlayDialog.prototype.open = function () {
   var self = this;
   var div = self.getElement();
   if (!$(div).hasClass("ui-dialog-content")) {
-    var buttons = [ {
-      text : "UPLOAD",
-      click : function() {
+    var buttons = [{
+      text: "UPLOAD",
+      click: function () {
         var dialog = this;
         var fileContent = self.getFileContent();
         if (fileContent === null) {
@@ -179,10 +182,10 @@ AddOverlayDialog.prototype.open = function() {
         } else if (fileContent.length > 1024 * 256) {
           GuiConnector.alert("File to big.<br>Please reduce file size or contact administrators.");
         } else {
-          return self.addOverlay().then(function(result) {
+          return self.addOverlay().then(function (result) {
             $(dialog).dialog("close");
             return result;
-          }, function(error) {
+          }, function (error) {
             GuiConnector.hideProcessing();
             if (error instanceof NetworkError && error.statusCode === HttpStatus.BAD_REQUEST) {
               var errorMessage = JSON.parse(error.content);
@@ -194,46 +197,20 @@ AddOverlayDialog.prototype.open = function() {
         }
       }
     }, {
-      text : "CANCEL",
-      click : function() {
+      text: "CANCEL",
+      click: function () {
         $(this).dialog("close");
       }
-    } ];
+    }];
 
     $(div).dialog({
-      title : "Add overlay",
-      buttons : buttons,
-      modal : true,
+      title: "Add overlay",
+      buttons: buttons,
+      modal: true,
     });
   }
 
   $(div).dialog("open");
 };
 
-AddOverlayDialog.prototype.parseFile = function(fileContent) {
-  var result = {};
-  var lines = fileContent.split("\n");
-  for (var i = 0; i < lines.length; i++) {
-    var line = lines[i];
-    if (line.startsWith("#")) {
-      if (line.indexOf("=") > 0) {
-        var name = line.substring(1, line.indexOf("=")).trim();
-        var value = line.substring(line.indexOf("=") + 1).trim();
-        if (name === "NAME") {
-          result.name = value;
-        } else if (name === "DESCRIPTION") {
-          result.description = value;
-        } else if (name === "TYPE") {
-          result.type = value;
-        }
-      } else {
-        logger.warn("Invalid overlay header line: " + line);
-      }
-    } else {
-      break;
-    }
-  }
-  return result;
-};
-
 module.exports = AddOverlayDialog;
diff --git a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js
index 26dea5dfd6dd9bdc3a4793fc5dead2f571d1f640..966d914461424cdbc1c4fb1672b782fa37b0c029 100644
--- a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js
+++ b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js
@@ -2,12 +2,14 @@
 
 /* exported logger */
 var Promise = require("bluebird");
+var JSZip = require("jszip");
 
 var AbstractGuiElement = require('../AbstractGuiElement');
 var ChooseAnnotatorsDialog = require('./ChooseAnnotatorsDialog');
 var ChooseValidatorsDialog = require('./ChooseValidatorsDialog');
 var GuiConnector = require('../../GuiConnector');
-var UserPreferences = require("../../map/data/UserPreferences");
+var OverlayParser = require('../../map/OverlayParser');
+var ZipEntry = require('./ZipEntry');
 
 var Functions = require('../../Functions');
 var logger = require('../../logger');
@@ -21,7 +23,10 @@ function AddProjectDialog(params) {
   AbstractGuiElement.call(this, params);
   var self = this;
   self.registerListenerType("onFileUpload");
+  self.registerListenerType("onZipFileUpload");
+  self.setZipEntries([]);
   $(self.getElement()).addClass("minerva-edit-project-dialog");
+  $(self.getElement()).css({overflow: "hidden"});
 
   self.createGui();
 }
@@ -36,7 +41,8 @@ AddProjectDialog.prototype.createGui = function () {
   var tabDiv = Functions.createElement({
     type: "div",
     name: "tabView",
-    className: "tabbable boxed parentTabs"
+    className: "tabbable boxed parentTabs",
+    style: "position:absolute;top:40px;bottom:10px;left:10px;right:10px"
   });
   element.appendChild(tabDiv);
 
@@ -48,7 +54,8 @@ AddProjectDialog.prototype.createGui = function () {
 
   var tabContentDiv = Functions.createElement({
     type: "div",
-    className: "tab-content"
+    className: "tab-content",
+    style: "height:100%"
   });
   tabDiv.appendChild(tabContentDiv);
 
@@ -83,6 +90,7 @@ AddProjectDialog.prototype.addTab = function (params) {
     navigationObject: navLi,
     navigationBar: params.tabMenuDiv
   });
+  contentDiv.style.overflow = "auto";
 
   if (params.content !== undefined) {
     contentDiv.appendChild(params.content);
@@ -148,6 +156,10 @@ AddProjectDialog.prototype.createGeneralTabContent = function () {
     var file = e.arg;
     return self.processFile(file);
   });
+  self.addListener("onFileUpload", function (e) {
+    var file = e.arg;
+    return self.setZipFileContent(file);
+  });
   self.addListener("onFileUpload", function (e) {
     var file = e.arg;
     if (file.name.lastIndexOf('.') > 0) {
@@ -308,11 +320,59 @@ AddProjectDialog.prototype.createOverlaysTabContent = function () {
 };
 
 AddProjectDialog.prototype._createOverlayTable = function () {
+  var self = this;
   var result = Functions.createElement({
     type: "div",
     style: "margin-top:10px;"
   });
 
+  var overlaysTable = Functions.createElement({
+    type: "table",
+    name: "overlaysTable",
+    className: "display",
+    style: "width:100%"
+  });
+  result.appendChild(overlaysTable);
+
+  $(overlaysTable).DataTable({
+    columns: [{
+      title: 'File name'
+    }, {
+      title: 'Name'
+    }, {
+      title: 'Description'
+    }]
+  });
+
+  $(overlaysTable).on("input", "[name='overlayName']", function () {
+    var input = this;
+    var filename = $(input).attr("data");
+    self.getEntryByFilename(filename).getData().name = $(input).val();
+  });
+
+  $(overlaysTable).on("input", "[name='overlayDescription']", function () {
+    var input = this;
+    var filename = $(input).attr("data");
+    self.getEntryByFilename(filename).getData().description = $(input).val();
+  });
+
+  self.addListener("onZipFileUpload", function () {
+    var entries = self.getZipEntries();
+    var dataTable = $($("[name='overlaysTable']", self.getElement())[0]).DataTable();
+    var data = [];
+    for (var i = 0; i < entries.length; i++) {
+      var entry = entries[i];
+      if (entry.getType() === "OVERLAY") {
+        var row = [];
+        row[0] = entry.getFilename();
+        row[1] = "<input data='" + entry.getFilename() + "' name='overlayName' value='" + entry.getData().name + "'/>";
+        row[2] = "<input data='" + entry.getFilename() + "' name='overlayDescription' value='" + entry.getData().description + "'/>";
+        data.push(row);
+      }
+    }
+    dataTable.clear().rows.add(data).draw();
+  });
+
   return result;
 };
 
@@ -321,17 +381,142 @@ AddProjectDialog.prototype.createSubmapsTab = function (tabMenuDiv, tabContentDi
   self.addTab({
     tabMenuDiv: tabMenuDiv,
     tabContentDiv: tabContentDiv,
-    name: "USERS",
+    name: "SUBMAPS",
     id: "project_submaps_tab",
     content: self.createSubmapsTabContent()
   });
 };
 
 AddProjectDialog.prototype.createSubmapsTabContent = function () {
+  var self = this;
   var result = Functions.createElement({
     type: "div",
     style: "margin-top:10px;"
   });
+  var submapsTable = Functions.createElement({
+    type: "table",
+    name: "submapsTable",
+    className: "display",
+    style: "width:100%"
+  });
+  result.appendChild(submapsTable);
+
+  $(submapsTable).DataTable({
+    columns: [{
+      title: 'File name'
+    }, {
+      title: 'Name'
+    }, {
+      title: 'Root map'
+    }, {
+      title: 'Mapping file'
+    }, {
+      title: 'Map type'
+    }]
+  });
+
+  $(submapsTable).on("input", "[name='submapName']", function () {
+    var input = this;
+    var filename = $(input).attr("data");
+    self.getEntryByFilename(filename).getData().name = $(input).val();
+  });
+
+  $(submapsTable).on("change", "[name='submapRoot']", function () {
+    var input = this;
+    if (!$(input).is(":checked")) {
+      this.checked = true;
+      GuiConnector.info("One model must be marked as a root");
+      return false;
+    }
+
+    var filename = $(input).attr("data");
+    var checkboxes = $("[name='submapRoot']", submapsTable);
+    for (var i = 0; i < checkboxes.length; i++) {
+      var checkbox = checkboxes[i];
+      if ($(checkbox).attr("data") !== filename) {
+        $(checkbox).attr('checked', false);
+        self.getEntryByFilename($(checkbox).attr("data")).getData().root = false;
+      }
+    }
+    self.getEntryByFilename(filename).getData().root = $(input).is(":checked");
+  });
+
+  $(submapsTable).on("change", "[name='submapMapping']", function () {
+    var input = this;
+    var filename = $(input).attr("data");
+    var checkboxes = $("[name='submapMapping']", submapsTable);
+    for (var i = 0; i < checkboxes.length; i++) {
+      var checkbox = checkboxes[i];
+      if ($(checkbox).attr("data") !== filename) {
+        $(checkbox).attr('checked', false);
+        self.getEntryByFilename($(checkbox).attr("data")).getData().mapping = false;
+      }
+    }
+    self.getEntryByFilename(filename).getData().mapping = $(input).is(":checked");
+  });
+
+  $(submapsTable).on("change", "[name='submapType']", function () {
+    var input = this;
+    var filename = $(input).attr("data");
+    return ServerConnector.getConfiguration().then(function (configuration) {
+      var mapTypes = configuration.getMapTypes();
+      for (var j = 0; j < mapTypes.length; j++) {
+        var mapType = mapTypes[j];
+        if (mapType.id === $(input).val()) {
+          self.getEntryByFilename(filename).getData().type = mapType;
+        }
+      }
+    });
+  });
+
+  self.addListener("onZipFileUpload", function () {
+    return ServerConnector.getConfiguration().then(function (configuration) {
+
+      var entries = self.getZipEntries();
+      var dataTable = $($("[name='submapsTable']", self.getElement())[0]).DataTable();
+      var data = [];
+      for (var i = 0; i < entries.length; i++) {
+        var entry = entries[i];
+        if (entry.getType() === "MAP") {
+          var row = [];
+          var rootCheckbox;
+          if (entry.getData().root) {
+            rootCheckbox = "<input name='submapRoot' type='checkbox' data='" + entry.getFilename() + "' checked='checked'/>";
+          } else {
+            rootCheckbox = "<input name='submapRoot' type='checkbox' data='" + entry.getFilename() + "'/>";
+          }
+          var mappingCheckbox;
+          if (entry.getData().mapping) {
+            mappingCheckbox = "<input name='submapMapping' type='checkbox' data='" + entry.getFilename() + "' checked='checked'/>";
+          } else {
+            mappingCheckbox = "<input name='submapMapping' type='checkbox' data='" + entry.getFilename() + "'/>";
+          }
+
+          var typeSelect = "<select data='" + entry.getFilename() + "' name='submapType'>";
+
+          typeSelect += "<option value='" + entry.getData().type.id + "' selected>" + entry.getData().type.name + "</option>";
+          var mapTypes = configuration.getMapTypes();
+          for (var j = 0; j < mapTypes.length; j++) {
+            var mapType = mapTypes[j];
+            if (mapType !== entry.getData().type) {
+              typeSelect += "<option value='" + mapType.id + "' >" + mapType.name + "</option>";
+            }
+          }
+          typeSelect += "</select>";
+
+
+          row[0] = entry.getFilename();
+          row[1] = "<input data='" + entry.getFilename() + "' name='submapName' value='" + entry.getData().name + "'/>";
+          row[2] = rootCheckbox;
+          row[3] = mappingCheckbox;
+          row[4] = typeSelect;
+          data.push(row);
+        }
+      }
+      dataTable.clear().rows.add(data).draw();
+    });
+  });
+
   return result;
 };
 
@@ -340,17 +525,48 @@ AddProjectDialog.prototype.createOverviewImagesTab = function (tabMenuDiv, tabCo
   self.addTab({
     tabMenuDiv: tabMenuDiv,
     tabContentDiv: tabContentDiv,
-    name: "USERS",
+    name: "IMAGES",
     id: "project_overview_images_tab",
     content: self.createOverviewImagesTabContent()
   });
 };
 
 AddProjectDialog.prototype.createOverviewImagesTabContent = function () {
+  var self = this;
   var result = Functions.createElement({
     type: "div",
     style: "margin-top:10px;"
   });
+
+  var imagesTable = Functions.createElement({
+    type: "table",
+    name: "imagesTable",
+    className: "display",
+    style: "width:100%"
+  });
+  result.appendChild(imagesTable);
+
+  $(imagesTable).DataTable({
+    columns: [{
+      title: 'File name'
+    }]
+  });
+
+  self.addListener("onZipFileUpload", function () {
+    var entries = self.getZipEntries();
+    var dataTable = $($("[name='imagesTable']", self.getElement())[0]).DataTable();
+    var data = [];
+    for (var i = 0; i < entries.length; i++) {
+      var entry = entries[i];
+      if (entry.getType() === "IMAGE") {
+        var row = [];
+        row[0] = entry.getFilename();
+        data.push(row);
+      }
+    }
+    dataTable.clear().rows.add(data).draw();
+  });
+
   return result;
 };
 
@@ -412,6 +628,20 @@ AddProjectDialog.prototype.destroy = function () {
   if (self._validatorsDialog !== undefined) {
     self._validatorsDialog.destroy();
   }
+  var overlaysTable = $("[name=overlaysTable]", self.getElement())[0];
+  if ($.fn.DataTable.isDataTable(overlaysTable)) {
+    $(overlaysTable).DataTable().destroy();
+  }
+  var submapsTable = $("[name=submapsTable]", self.getElement())[0];
+  if ($.fn.DataTable.isDataTable(submapsTable)) {
+    $(submapsTable).DataTable().destroy();
+  }
+
+  var imagesTable = $("[name=imagesTable]", self.getElement())[0];
+  if ($.fn.DataTable.isDataTable(imagesTable)) {
+    $(imagesTable).DataTable().destroy();
+  }
+
 };
 
 AddProjectDialog.prototype.open = function () {
@@ -445,7 +675,7 @@ AddProjectDialog.prototype.processFile = function (file) {
   if (file) {
     return new Promise(function (resolve, reject) {
       var reader = new FileReader();
-      reader.readAsText(file, "UTF-8");
+      reader.readAsText(file);
       reader.onload = function (evt) {
         try {
           self.setFileContent(evt.target.result);
@@ -464,6 +694,7 @@ AddProjectDialog.prototype.processFile = function (file) {
 };
 
 AddProjectDialog.prototype.setFileContent = function (fileContent) {
+  logger.debug(fileContent);
   this._fileContent = fileContent;
 };
 AddProjectDialog.prototype.getFileContent = function () {
@@ -593,7 +824,7 @@ AddProjectDialog.prototype.onSaveClicked = function () {
       "semantic-zoom": self.isSemanticZooming(),
       "annotate": self.isAnnotateAutomatically(),
       "verify-annotations": self.isVerifyAnnotations(),
-
+      "zip-entries": self.getZipEntries()
     };
     return ServerConnector.addProject(options);
   });
@@ -627,4 +858,124 @@ AddProjectDialog.prototype.checkValidity = function () {
   });
 };
 
+AddProjectDialog.prototype.setZipFileContent = function (file) {
+  var self = this;
+  if (file.name.toLowerCase().endsWith("zip")) {
+    var jsZip = new JSZip();
+    return jsZip.loadAsync(file).then(function (zip) {
+      var files = zip.files;
+      var promises = [];
+      var entries = [];
+      for (var key in files) {
+        if (files.hasOwnProperty(key)) {
+          promises.push(self.createZipEntry(files[key], zip));
+        }
+      }
+      return Promise.all(promises).then(function (result) {
+        for (var i = 0; i < result.length; i++) {
+          if (result[i] !== null) {
+            entries.push(result[i]);
+          }
+        }
+        return self.setZipEntries(entries);
+      });
+    });
+  } else {
+    return self.setZipEntries([]);
+  }
+};
+
+AddProjectDialog.prototype.createZipEntry = function (jsZipEntry, zipObject) {
+  if (jsZipEntry.dir) {
+    return null;
+  }
+  var filename = jsZipEntry.name.toLowerCase();
+  var type = null;
+  var data = {};
+  var processingPromise = Promise.resolve();
+  if (filename.startsWith("submaps")) {
+    type = "MAP";
+    if (filename.endsWith("mapping.xml")) {
+      data.mapping = true;
+    }
+  } else if (filename.startsWith("images")) {
+    type = "IMAGE";
+  } else if (filename.startsWith("layouts") || filename.startsWith("overlays")) {
+    type = "OVERLAY";
+    processingPromise = zipObject.file(jsZipEntry.name).async("string").then(function (content) {
+      var overlayParser = new OverlayParser();
+      var overlay = overlayParser.parse(content);
+      if (overlay.getName()) {
+        data.name = overlay.getName();
+      } else {
+        data.name = "";
+      }
+      if (overlay.getDescription()) {
+        data.description = overlay.getDescription();
+      } else {
+        data.description = "";
+      }
+    });
+  } else if (filename.indexOf("\\") === -1 && filename.indexOf("/") === -1) {
+    type = "MAP";
+    data.root = true;
+  } else {
+    throw new Error("Unrecognized file: " + filename);
+  }
+  if (type === "MAP") {
+    var name = jsZipEntry.name.toLowerCase();
+    this.setFileParserForFilename(name);
+    if (name.indexOf(".") > 0) {
+      name = name.substr(0, name.indexOf("."));
+    }
+    if (name.lastIndexOf("\\") >= 0) {
+      name = name.substr(name.lastIndexOf("\\") + 1);
+    }
+    if (name.lastIndexOf("/") >= 0) {
+      name = name.substr(name.lastIndexOf("/") + 1);
+    }
+    data.name = name;
+
+    processingPromise = processingPromise.then(function () {
+      return ServerConnector.getConfiguration().then(function (configuration) {
+        var mapTypes = configuration.getMapTypes();
+        for (var i = 0; i < mapTypes.length; i++) {
+          if (mapTypes[i].id === "UNKNOWN") {
+            data.type = mapTypes[i];
+          }
+        }
+        if (data.type === undefined) {
+          data.type = mapTypes[0];
+        }
+      });
+    });
+  }
+
+  return processingPromise.then(function () {
+    return new ZipEntry({filename: filename, type: type, data: data});
+  });
+};
+
+AddProjectDialog.prototype.getZipEntries = function () {
+  return this._zipEntries;
+};
+AddProjectDialog.prototype.setZipEntries = function (entries) {
+  var self = this;
+  self._zipEntries = entries;
+  return self.callListeners("onZipFileUpload", entries);
+
+};
+
+AddProjectDialog.prototype.getEntryByFilename = function (filename) {
+  var self = this;
+  var entries = self.getZipEntries();
+  for (var i = 0; i < entries.length; i++) {
+    var entry = entries[i];
+    if (entry.getFilename() === filename) {
+      return entry;
+    }
+  }
+  return null;
+};
+
 module.exports = AddProjectDialog;
diff --git a/frontend-js/src/main/js/gui/admin/ZipEntry.js b/frontend-js/src/main/js/gui/admin/ZipEntry.js
new file mode 100644
index 0000000000000000000000000000000000000000..c3891a3a013e1b131353649c320471edd8671b7c
--- /dev/null
+++ b/frontend-js/src/main/js/gui/admin/ZipEntry.js
@@ -0,0 +1,37 @@
+"use strict";
+
+var types = ["IMAGE", "OVERLAY", "MAP"];
+
+function ZipEntry(params) {
+  var self = this;
+  self.setType(params.type);
+  self.setFilename(params.filename);
+  self.setData(params.data);
+}
+
+ZipEntry.prototype.setType = function (type) {
+  if (types.indexOf(type) === -1) {
+    throw new Error("Unknown ZipEntryType: " + type + ".")
+  }
+  this._type = type;
+};
+ZipEntry.prototype.getType = function () {
+  return this._type;
+};
+
+ZipEntry.prototype.setFilename = function (filename) {
+  this._filename = filename;
+};
+
+ZipEntry.prototype.getFilename = function () {
+  return this._filename;
+};
+
+ZipEntry.prototype.setData = function (data) {
+  this._data = data;
+};
+ZipEntry.prototype.getData = function () {
+  return this._data;
+};
+
+module.exports = ZipEntry;
diff --git a/frontend-js/src/main/js/map/OverlayParser.js b/frontend-js/src/main/js/map/OverlayParser.js
new file mode 100644
index 0000000000000000000000000000000000000000..31fc9bc086d2884189449f5c1b9c4bacc07d1e2c
--- /dev/null
+++ b/frontend-js/src/main/js/map/OverlayParser.js
@@ -0,0 +1,37 @@
+"use strict";
+
+var logger = require('./../logger');
+var LayoutData = require('./data/LayoutData');
+
+function OverlayParser() {
+}
+
+OverlayParser.prototype.parse = function (content) {
+  var data = {content: content};
+  var lines = content.split("\n");
+  for (var i = 0; i < lines.length; i++) {
+    var line = lines[i];
+    if (line.startsWith("#")) {
+      if (line.indexOf("=") > 0) {
+        var name = line.substring(1, line.indexOf("=")).trim();
+        var value = line.substring(line.indexOf("=") + 1).trim();
+        if (name === "NAME") {
+          data.name = value;
+        } else if (name === "DESCRIPTION") {
+          data.description = value;
+        } else if (name === "TYPE") {
+          data.type = value;
+        }
+      } else {
+        logger.warn("Invalid overlay header line: " + line);
+      }
+    } else {
+      break;
+    }
+  }
+
+  return new LayoutData(data);
+};
+
+
+module.exports = OverlayParser;
diff --git a/frontend-js/src/main/js/map/data/LayoutData.js b/frontend-js/src/main/js/map/data/LayoutData.js
index 8d8c29ba94f124cee4f4eb3394a0a187c4492e94..2e2fa6d043921a6775537c2e2c06d08dec809113 100644
--- a/frontend-js/src/main/js/map/data/LayoutData.js
+++ b/frontend-js/src/main/js/map/data/LayoutData.js
@@ -27,6 +27,7 @@ function LayoutData(layoutId, name) {
     this.setFilename(object.filename);
     this.setPublicOverlay(object.publicOverlay);
     this.setInputDataAvailable(object.inputDataAvailable);
+    this.setType(object.type);
     if (!this.getInputDataAvailable()) {
       this.setInitialized(true);
     }
@@ -42,54 +43,54 @@ function LayoutData(layoutId, name) {
 
 /**
  * Adds alias to the {@link LayoutData}
- * 
+ *
  * @param layoutAlias
  *          information about alias in a layout
  */
-LayoutData.prototype.addAlias = function(layoutAlias) {
+LayoutData.prototype.addAlias = function (layoutAlias) {
   this.aliases.push(layoutAlias);
   this.aliasById[layoutAlias.getId()] = layoutAlias;
 };
 
 /**
  * Adds reaction to the {@link LayoutData}
- * 
+ *
  * @param layoutReaction
  *          information about reaction in a layout
  */
-LayoutData.prototype.addReaction = function(layoutReaction) {
+LayoutData.prototype.addReaction = function (layoutReaction) {
   this.reactions.push(layoutReaction);
 };
 
-LayoutData.prototype.getId = function() {
+LayoutData.prototype.getId = function () {
   return this.id;
 };
 
-LayoutData.prototype.setId = function(id) {
+LayoutData.prototype.setId = function (id) {
   this.id = parseInt(id);
 };
 
-LayoutData.prototype.getDescription = function() {
+LayoutData.prototype.getDescription = function () {
   return this._description;
 };
 
-LayoutData.prototype.setDescription = function(description) {
+LayoutData.prototype.setDescription = function (description) {
   this._description = description;
 };
 
-LayoutData.prototype.getCreator = function() {
+LayoutData.prototype.getCreator = function () {
   return this._creator;
 };
 
-LayoutData.prototype.setCreator = function(creator) {
+LayoutData.prototype.setCreator = function (creator) {
   this._creator = creator;
 };
 
-LayoutData.prototype.getInputDataAvailable = function() {
+LayoutData.prototype.getInputDataAvailable = function () {
   return this._inputDataAvailable;
 };
 
-LayoutData.prototype.setInputDataAvailable = function(inputDataAvailable) {
+LayoutData.prototype.setInputDataAvailable = function (inputDataAvailable) {
   var value = inputDataAvailable;
   if (inputDataAvailable === undefined) {
     value = false;
@@ -104,23 +105,23 @@ LayoutData.prototype.setInputDataAvailable = function(inputDataAvailable) {
   this._inputDataAvailable = value;
 };
 
-LayoutData.prototype.getName = function() {
+LayoutData.prototype.getName = function () {
   return this.name;
 };
 
-LayoutData.prototype.setName = function(name) {
+LayoutData.prototype.setName = function (name) {
   this.name = name;
 };
 
-LayoutData.prototype.getDirectory = function() {
+LayoutData.prototype.getDirectory = function () {
   return this._directory;
 };
 
-LayoutData.prototype.setDirectory = function(directory) {
+LayoutData.prototype.setDirectory = function (directory) {
   this._directory = directory;
 };
 
-LayoutData.prototype.updateAlias = function(layoutAlias) {
+LayoutData.prototype.updateAlias = function (layoutAlias) {
   if (this.aliasById[layoutAlias.getId()] === undefined) {
     logger.warn("Cannot update alias, it doesn't exist. Alias: ", layoutAlias.getId());
   } else {
@@ -129,19 +130,19 @@ LayoutData.prototype.updateAlias = function(layoutAlias) {
 
 };
 
-LayoutData.prototype.getAliasById = function(id) {
+LayoutData.prototype.getAliasById = function (id) {
   return this.aliasById[id];
 };
 
-LayoutData.prototype.getFullAliasById = function(id) {
+LayoutData.prototype.getFullAliasById = function (id) {
   var self = this;
   var alias = self.getAliasById(id);
   if (alias !== undefined) {
     if (alias.getType() === LayoutAlias.LIGTH) {
       return ServerConnector.getFullOverlayElement({
-        element : new IdentifiedElement(alias),
-        overlay : self,
-      }).then(function(data) {
+        element: new IdentifiedElement(alias),
+        overlay: self,
+      }).then(function (data) {
         self.updateAlias(data);
         return alias;
       });
@@ -150,28 +151,28 @@ LayoutData.prototype.getFullAliasById = function(id) {
   return Promise.resolve(alias);
 };
 
-LayoutData.prototype.setInitialized = function(value) {
+LayoutData.prototype.setInitialized = function (value) {
   this._initialized = value;
 };
 
-LayoutData.prototype.isInitialized = function() {
+LayoutData.prototype.isInitialized = function () {
   return this._initialized;
 };
 
-LayoutData.prototype.getAliases = function() {
+LayoutData.prototype.getAliases = function () {
   return this.aliases;
 };
 
-LayoutData.prototype.getReactions = function() {
+LayoutData.prototype.getReactions = function () {
   return this.reactions;
 };
 
-LayoutData.prototype.init = function() {
+LayoutData.prototype.init = function () {
   var self = this;
   if (this.isInitialized()) {
     return Promise.resolve();
   }
-  return ServerConnector.getOverlayElements(self.getId()).then(function(data) {
+  return ServerConnector.getOverlayElements(self.getId()).then(function (data) {
     for (var i = 0; i < data.length; i++) {
       if (data[i] instanceof LayoutAlias) {
         self.addAlias(data[i]);
@@ -187,28 +188,36 @@ LayoutData.prototype.init = function() {
 
 };
 
-LayoutData.prototype.getPublicOverlay = function() {
+LayoutData.prototype.getPublicOverlay = function () {
   return this._publicOverlay;
 };
 
-LayoutData.prototype.setPublicOverlay = function(publicOverlay) {
+LayoutData.prototype.setPublicOverlay = function (publicOverlay) {
   this._publicOverlay = publicOverlay;
 };
 
-LayoutData.prototype.getContent = function() {
+LayoutData.prototype.getContent = function () {
   return this._content;
 };
 
-LayoutData.prototype.setContent = function(content) {
+LayoutData.prototype.setContent = function (content) {
   this._content = content;
 };
 
-LayoutData.prototype.getFilename = function() {
+LayoutData.prototype.getFilename = function () {
   return this._filename;
 };
 
-LayoutData.prototype.setFilename= function(filename) {
+LayoutData.prototype.setFilename = function (filename) {
   this._filename = filename;
 };
 
+LayoutData.prototype.getType = function () {
+  return this._type;
+};
+
+LayoutData.prototype.setType = function (type) {
+  this._type = type;
+};
+
 module.exports = LayoutData;
diff --git a/frontend-js/src/test/js/gui/AddOverlayDialog-test.js b/frontend-js/src/test/js/gui/AddOverlayDialog-test.js
index b9700d844df392ad345f93cc76c5bc4b49060e01..26b190671ff0b2a9a19e5d807e1f8ad46559dbc6 100644
--- a/frontend-js/src/test/js/gui/AddOverlayDialog-test.js
+++ b/frontend-js/src/test/js/gui/AddOverlayDialog-test.js
@@ -12,23 +12,6 @@ var logger = require('../logger');
 
 describe('AddOverlayDialog', function() {
 
-  describe('processFile', function() {
-    it('default', function() {
-      return ServerConnector.getProject().then(function(project) {
-        var dialog = new AddOverlayDialog({
-          element : testDiv,
-          project : project,
-          customMap : null
-        });
-
-        var file = new Blob([ "#DESCRIPTION=xxx\nname\tvalue\ns1\t1" ]);
-        return dialog.processFile(file).then(function(content) {
-          assert.ok(content !== null);
-        });
-      });
-    });
-  });
-
   it('addOverlay', function() {
     var dialog;
     return ServerConnector.getProject().then(function(project) {
@@ -43,22 +26,4 @@ describe('AddOverlayDialog', function() {
     });
   });
 
-  
-  it('parse overlay file', function() {
-    return ServerConnector.getProject().then(function(project) {
-      var dialog = new AddOverlayDialog({
-        element : testDiv,
-        project : project,
-        customMap : null
-      });
-
-      return ServerConnector.sendGetRequest("testFiles/overlay/good.txt").then(function(fileContent) {
-        var obj = dialog.parseFile(fileContent);
-        assert.equal(obj.name, "example name");
-        assert.equal(obj.description, "layout description");
-        assert.equal(obj.type, "GENERIC");
-      });
-    });
-  });
-
 });
diff --git a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js
index 50d53689717e43f64af90a583f190e09f7f2ba0d..219804ccf82bbf0ebbaabdd3a1e6310c06c6a63f 100644
--- a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js
+++ b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js
@@ -7,6 +7,7 @@ require("../../mocha-config");
 var AddProjectDialog = require('../../../../main/js/gui/admin/AddProjectDialog');
 var logger = require('../../logger');
 
+var fs = require("fs");
 var chai = require('chai');
 var assert = chai.assert;
 
@@ -20,6 +21,7 @@ describe('AddProjectDialog', function () {
       return dialog.init();
     }).then(function () {
       assert.ok(dialog.getNotifyEmail() !== "");
+      return dialog.destroy();
     });
   });
 
@@ -35,6 +37,7 @@ describe('AddProjectDialog', function () {
       assert.notOk(dialog.isCache());
       dialog.setCache(true);
       assert.ok(dialog.isCache());
+      return dialog.destroy();
     });
   });
 
@@ -55,6 +58,41 @@ describe('AddProjectDialog', function () {
         return dialog.getConverter();
       }).then(function (converter) {
         assert.equal("xml", converter.extension);
+        return dialog.destroy();
+      });
+    });
+  });
+
+  describe('setZipFileContent', function () {
+    it('submaps', function () {
+      var dialog = new AddProjectDialog({
+        element: testDiv,
+        customMap: null
+      });
+      var buf = fs.readFileSync("testFiles/map/complex_model_with_submaps.zip");
+      buf.name = "complex_model_with_submaps.zip";
+      return dialog.init().then(function () {
+        return dialog.setZipFileContent(buf);
+      }).then(function () {
+        assert.equal(5, dialog.getZipEntries().length);
+        return dialog.destroy();
+      });
+    });
+    it('overlays', function () {
+      var dialog = new AddProjectDialog({
+        element: testDiv,
+        customMap: null
+      });
+      var buf = fs.readFileSync("testFiles/map/complex_model_with_overlays.zip");
+      buf.name = "complex_model_with_overlays.zip";
+      return dialog.init().then(function () {
+        var dataTable = $($("[name='overlaysTable']", testDiv)[0]).DataTable();
+        assert.equal(0, dataTable.data().count());
+        return dialog.setZipFileContent(buf);
+      }).then(function () {
+        var dataTable = $($("[name='overlaysTable']", testDiv)[0]).DataTable();
+        assert.ok(dataTable.data().count() > 0);
+        return dialog.destroy();
       });
     });
   });
@@ -67,7 +105,7 @@ describe('AddProjectDialog', function () {
       });
 
       return dialog.showAnnotatorsDialog().then(function () {
-        dialog.destroy();
+        return dialog.destroy();
       });
     });
   });
@@ -110,6 +148,7 @@ describe('AddProjectDialog', function () {
         assert.ok(options["organism"] !== undefined);
         assert.ok(options["sbgn"] !== undefined);
         assert.ok(options["semantic-zoom"] !== undefined);
+        return dialog.destroy();
       });
     });
   });
diff --git a/frontend-js/src/test/js/map/OverlayParser-test.js b/frontend-js/src/test/js/map/OverlayParser-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..b3a0f0ae080204da617af2e331435a316882c07a
--- /dev/null
+++ b/frontend-js/src/test/js/map/OverlayParser-test.js
@@ -0,0 +1,33 @@
+"use strict";
+
+require("../mocha-config.js");
+
+var OverlayParser = require('../../../main/js/map/OverlayParser');
+var chai = require('chai');
+var assert = chai.assert;
+
+describe('OverlayParser', function () {
+  describe('parse', function () {
+    it('simple', function () {
+      var parser = new OverlayParser();
+      var fileContent = "#NAME=some Name\n#DESCRIPTION=xxx\nname\tvalue\ns1\t1";
+
+      var overlay = parser.parse(fileContent);
+      assert.ok(overlay);
+      assert.equal(overlay.getDescription(), "xxx");
+      assert.equal(overlay.getName(), "some Name");
+      assert.ok(overlay.getContent());
+    });
+    it('with type', function () {
+
+      return ServerConnector.sendGetRequest("testFiles/overlay/good.txt").then(function (fileContent) {
+        var parser = new OverlayParser();
+        var overlay = parser.parse(fileContent);
+        assert.equal(overlay.getName(), "example name");
+        assert.equal(overlay.getDescription(), "layout description");
+        assert.equal(overlay.getType(), "GENERIC");
+      });
+    });
+  });
+
+});
diff --git a/frontend-js/testFiles/apiCalls/configuration/token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/configuration/token=MOCK_TOKEN_ID&
index 87bada811f18fa43905db6a71d4b83047d186854..38618b8344d16fc18518b9dfd5cda7551f015063 100644
--- a/frontend-js/testFiles/apiCalls/configuration/token=MOCK_TOKEN_ID&
+++ b/frontend-js/testFiles/apiCalls/configuration/token=MOCK_TOKEN_ID&
@@ -1 +1 @@
-{"modelFormats":[{"handler":"lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser","extension":"xml","name":"CellDesigner SBML"},{"handler":"lcsb.mapviewer.converter.model.sbgnml.SbgnmlXmlConverter","extension":"sbgn","name":"SBGN-ML"}],"elementTypes":[{"name":"Degraded","className":"lcsb.mapviewer.model.map.species.Degraded","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.LeftSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.IonChannelProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.TopSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Ion","className":"lcsb.mapviewer.model.map.species.Ion","parentClass":"lcsb.mapviewer.model.map.species.Chemical"},{"name":"Species","className":"lcsb.mapviewer.model.map.species.Species","parentClass":"lcsb.mapviewer.model.map.species.Element"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.RightSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Drug","className":"lcsb.mapviewer.model.map.species.Drug","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.Protein","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.TruncatedProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.PathwayCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.BottomSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"RNA","className":"lcsb.mapviewer.model.map.species.Rna","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Chemical","className":"lcsb.mapviewer.model.map.species.Chemical","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.Compartment","parentClass":"lcsb.mapviewer.model.map.species.Element"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.OvalCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.SquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Unknown","className":"lcsb.mapviewer.model.map.species.Unknown","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Element","className":"lcsb.mapviewer.model.map.species.Element","parentClass":"lcsb.mapviewer.model.map.BioEntity"},{"name":"Phenotype","className":"lcsb.mapviewer.model.map.species.Phenotype","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Complex","className":"lcsb.mapviewer.model.map.species.Complex","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Antisense RNA","className":"lcsb.mapviewer.model.map.species.AntisenseRna","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.ReceptorProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Simple molecule","className":"lcsb.mapviewer.model.map.species.SimpleMolecule","parentClass":"lcsb.mapviewer.model.map.species.Chemical"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.GenericProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Gene","className":"lcsb.mapviewer.model.map.species.Gene","parentClass":"lcsb.mapviewer.model.map.species.Species"}],"modificationStateTypes":{"PHOSPHORYLATED":{"commonName":"phosphorylated","abbreviation":"P"},"METHYLATED":{"commonName":"methylated","abbreviation":"Me"},"PALMYTOYLATED":{"commonName":"palmytoylated","abbreviation":"Pa"},"ACETYLATED":{"commonName":"acetylated","abbreviation":"Ac"},"SULFATED":{"commonName":"sulfated","abbreviation":"S"},"GLYCOSYLATED":{"commonName":"glycosylated","abbreviation":"G"},"PRENYLATED":{"commonName":"prenylated","abbreviation":"Pr"},"UBIQUITINATED":{"commonName":"ubiquitinated","abbreviation":"Ub"},"PROTONATED":{"commonName":"protonated","abbreviation":"H"},"HYDROXYLATED":{"commonName":"hydroxylated","abbreviation":"OH"},"MYRISTOYLATED":{"commonName":"myristoylated","abbreviation":"My"},"UNKNOWN":{"commonName":"unknown","abbreviation":"?"},"EMPTY":{"commonName":"empty","abbreviation":""},"DONT_CARE":{"commonName":"don't care","abbreviation":"*"}},"miriamTypes":{"CHEMBL_TARGET":{"commonName":"ChEMBL target","uris":["urn:miriam:chembl.target"],"homepage":"https://www.ebi.ac.uk/chembldb/","registryIdentifier":"MIR:00000085"},"UNIPROT":{"commonName":"Uniprot","uris":["urn:miriam:uniprot"],"homepage":"http://www.uniprot.org/","registryIdentifier":"MIR:00000005"},"MI_R_BASE_MATURE_SEQUENCE":{"commonName":"miRBase Mature Sequence Database","uris":["urn:miriam:mirbase.mature"],"homepage":"http://www.mirbase.org/","registryIdentifier":"MIR:00000235"},"PFAM":{"commonName":"Protein Family Database","uris":["urn:miriam:pfam"],"homepage":"http://pfam.xfam.org//","registryIdentifier":"MIR:00000028"},"ENSEMBL_PLANTS":{"commonName":"Ensembl Plants","uris":["urn:miriam:ensembl.plant"],"homepage":"http://plants.ensembl.org/","registryIdentifier":"MIR:00000205"},"WIKIPEDIA":{"commonName":"Wikipedia (English)","uris":["urn:miriam:wikipedia.en"],"homepage":"http://en.wikipedia.org/wiki/Main_Page","registryIdentifier":"MIR:00000384"},"CHEBI":{"commonName":"Chebi","uris":["urn:miriam:obo.chebi","urn:miriam:chebi"],"homepage":"http://www.ebi.ac.uk/chebi/","registryIdentifier":"MIR:00000002"},"WIKIDATA":{"commonName":"Wikidata","uris":["urn:miriam:wikidata"],"homepage":"https://www.wikidata.org/","registryIdentifier":"MIR:00000549"},"REACTOME":{"commonName":"Reactome","uris":["urn:miriam:reactome"],"homepage":"http://www.reactome.org/","registryIdentifier":"MIR:00000018"},"EC":{"commonName":"Enzyme Nomenclature","uris":["urn:miriam:ec-code"],"homepage":"http://www.enzyme-database.org/","registryIdentifier":"MIR:00000004"},"UNIPROT_ISOFORM":{"commonName":"UniProt Isoform","uris":["urn:miriam:uniprot.isoform"],"homepage":"http://www.uniprot.org/","registryIdentifier":"MIR:00000388"},"OMIM":{"commonName":"Online Mendelian Inheritance in Man","uris":["urn:miriam:omim"],"homepage":"http://omim.org/","registryIdentifier":"MIR:00000016"},"DRUGBANK_TARGET_V4":{"commonName":"DrugBank Target v4","uris":["urn:miriam:drugbankv4.target"],"homepage":"http://www.drugbank.ca/targets","registryIdentifier":"MIR:00000528"},"MIR_TAR_BASE_MATURE_SEQUENCE":{"commonName":"miRTarBase Mature Sequence Database","uris":["urn:miriam:mirtarbase"],"homepage":"http://mirtarbase.mbc.nctu.edu.tw/","registryIdentifier":"MIR:00100739"},"CHEMBL_COMPOUND":{"commonName":"ChEMBL","uris":["urn:miriam:chembl.compound"],"homepage":"https://www.ebi.ac.uk/chembldb/","registryIdentifier":"MIR:00000084"},"KEGG_PATHWAY":{"commonName":"Kegg Pathway","uris":["urn:miriam:kegg.pathway"],"homepage":"http://www.genome.jp/kegg/pathway.html","registryIdentifier":"MIR:00000012"},"CAS":{"commonName":"Chemical Abstracts Service","uris":["urn:miriam:cas"],"homepage":"http://commonchemistry.org","registryIdentifier":"MIR:00000237"},"REFSEQ":{"commonName":"RefSeq","uris":["urn:miriam:refseq"],"homepage":"http://www.ncbi.nlm.nih.gov/projects/RefSeq/","registryIdentifier":"MIR:00000039"},"WORM_BASE":{"commonName":"WormBase","uris":["urn:miriam:wormbase"],"homepage":"http://wormbase.bio2rdf.org/fct","registryIdentifier":"MIR:00000027"},"MI_R_BASE_SEQUENCE":{"commonName":"miRBase Sequence Database","uris":["urn:miriam:mirbase"],"homepage":"http://www.mirbase.org/","registryIdentifier":"MIR:00000078"},"TAIR_LOCUS":{"commonName":"TAIR Locus","uris":["urn:miriam:tair.locus"],"homepage":"http://arabidopsis.org/index.jsp","registryIdentifier":"MIR:00000050"},"PHARM":{"commonName":"PharmGKB Pathways","uris":["urn:miriam:pharmgkb.pathways"],"homepage":"http://www.pharmgkb.org/","registryIdentifier":"MIR:00000089"},"PANTHER":{"commonName":"PANTHER Family","uris":["urn:miriam:panther.family","urn:miriam:panther"],"homepage":"http://www.pantherdb.org/","registryIdentifier":"MIR:00000060"},"TAXONOMY":{"commonName":"Taxonomy","uris":["urn:miriam:taxonomy"],"homepage":"http://www.ncbi.nlm.nih.gov/taxonomy/","registryIdentifier":"MIR:00000006"},"UNIGENE":{"commonName":"UniGene","uris":["urn:miriam:unigene"],"homepage":"http://www.ncbi.nlm.nih.gov/unigene","registryIdentifier":"MIR:00000346"},"HGNC":{"commonName":"HGNC","uris":["urn:miriam:hgnc"],"homepage":"http://www.genenames.org","registryIdentifier":"MIR:00000080"},"HGNC_SYMBOL":{"commonName":"HGNC Symbol","uris":["urn:miriam:hgnc.symbol"],"homepage":"http://www.genenames.org","registryIdentifier":"MIR:00000362"},"COG":{"commonName":"Clusters of Orthologous Groups","uris":["urn:miriam:cogs"],"homepage":"https://www.ncbi.nlm.nih.gov/COG/","registryIdentifier":"MIR:00000296"},"WIKIPATHWAYS":{"commonName":"WikiPathways","uris":["urn:miriam:wikipathways"],"homepage":"http://www.wikipathways.org/","registryIdentifier":"MIR:00000076"},"HMDB":{"commonName":"HMDB","uris":["urn:miriam:hmdb"],"homepage":"http://www.hmdb.ca/","registryIdentifier":"MIR:00000051"},"CHEMSPIDER":{"commonName":"ChemSpider","uris":["urn:miriam:chemspider"],"homepage":"http://www.chemspider.com//","registryIdentifier":"MIR:00000138"},"ENSEMBL":{"commonName":"Ensembl","uris":["urn:miriam:ensembl"],"homepage":"www.ensembl.org","registryIdentifier":"MIR:00000003"},"GO":{"commonName":"Gene Ontology","uris":["urn:miriam:obo.go","urn:miriam:go"],"homepage":"http://amigo.geneontology.org/amigo","registryIdentifier":"MIR:00000022"},"KEGG_REACTION":{"commonName":"Kegg Reaction","uris":["urn:miriam:kegg.reaction"],"homepage":"http://www.genome.jp/kegg/reaction/","registryIdentifier":"MIR:00000014"},"KEGG_ORTHOLOGY":{"commonName":"KEGG Orthology","uris":["urn:miriam:kegg.orthology"],"homepage":"http://www.genome.jp/kegg/ko.html","registryIdentifier":"MIR:00000116"},"PUBCHEM":{"commonName":"PubChem-compound","uris":["urn:miriam:pubchem.compound"],"homepage":"http://pubchem.ncbi.nlm.nih.gov/","registryIdentifier":"MIR:00000034"},"MESH_2012":{"commonName":"MeSH 2012","uris":["urn:miriam:mesh.2012","urn:miriam:mesh"],"homepage":"http://www.nlm.nih.gov/mesh/","registryIdentifier":"MIR:00000270"},"MGD":{"commonName":"Mouse Genome Database","uris":["urn:miriam:mgd"],"homepage":"http://www.informatics.jax.org/","registryIdentifier":"MIR:00000037"},"ENTREZ":{"commonName":"Entrez Gene","uris":["urn:miriam:ncbigene","urn:miriam:entrez.gene"],"homepage":"http://www.ncbi.nlm.nih.gov/gene","registryIdentifier":"MIR:00000069"},"PUBCHEM_SUBSTANCE":{"commonName":"PubChem-substance","uris":["urn:miriam:pubchem.substance"],"homepage":"http://pubchem.ncbi.nlm.nih.gov/","registryIdentifier":"MIR:00000033"},"CCDS":{"commonName":"Consensus CDS","uris":["urn:miriam:ccds"],"homepage":"http://www.ncbi.nlm.nih.gov/CCDS/","registryIdentifier":"MIR:00000375"},"KEGG_GENES":{"commonName":"Kegg Genes","uris":["urn:miriam:kegg.genes","urn:miriam:kegg.genes:hsa"],"homepage":"http://www.genome.jp/kegg/genes.html","registryIdentifier":"MIR:00000070"},"TOXICOGENOMIC_CHEMICAL":{"commonName":"Toxicogenomic Chemical","uris":["urn:miriam:ctd.chemical"],"homepage":"http://ctdbase.org/","registryIdentifier":"MIR:00000098"},"SGD":{"commonName":"Saccharomyces Genome Database","uris":["urn:miriam:sgd"],"homepage":"http://www.yeastgenome.org/","registryIdentifier":"MIR:00000023"},"KEGG_COMPOUND":{"commonName":"Kegg Compound","uris":["urn:miriam:kegg.compound"],"homepage":"http://www.genome.jp/kegg/ligand.html","registryIdentifier":"MIR:00000013"},"INTERPRO":{"commonName":"InterPro","uris":["urn:miriam:interpro"],"homepage":"http://www.ebi.ac.uk/interpro/","registryIdentifier":"MIR:00000011"},"UNKNOWN":{"commonName":"Unknown","uris":[],"homepage":null,"registryIdentifier":null},"DRUGBANK":{"commonName":"DrugBank","uris":["urn:miriam:drugbank"],"homepage":"http://www.drugbank.ca/","registryIdentifier":"MIR:00000102"},"PUBMED":{"commonName":"PubMed","uris":["urn:miriam:pubmed"],"homepage":"http://www.ncbi.nlm.nih.gov/PubMed/","registryIdentifier":"MIR:00000015"}},"imageFormats":[{"handler":"lcsb.mapviewer.converter.graphics.PngImageGenerator","extension":"png","name":"PNG image"},{"handler":"lcsb.mapviewer.converter.graphics.PdfImageGenerator","extension":"pdf","name":"PDF"},{"handler":"lcsb.mapviewer.converter.graphics.SvgImageGenerator","extension":"svg","name":"SVG image"}],"annotators":[{"name":"Biocompendium","className":"lcsb.mapviewer.annotation.services.annotators.BiocompendiumAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Protein"],"url":"http://biocompendium.embl.de/"},{"name":"Chebi","className":"lcsb.mapviewer.annotation.services.annotators.ChebiAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Chemical"],"url":"http://www.ebi.ac.uk/chebi/"},{"name":"Uniprot","className":"lcsb.mapviewer.annotation.services.annotators.UniprotAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Gene","lcsb.mapviewer.model.map.species.Rna"],"url":"http://www.uniprot.org/"},{"name":"Gene Ontology","className":"lcsb.mapviewer.annotation.services.annotators.GoAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Phenotype","lcsb.mapviewer.model.map.compartment.Compartment","lcsb.mapviewer.model.map.species.Complex"],"url":"http://amigo.geneontology.org/amigo"},{"name":"HGNC","className":"lcsb.mapviewer.annotation.services.annotators.HgncAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"http://www.genenames.org"},{"name":"Recon annotator","className":"lcsb.mapviewer.annotation.services.annotators.ReconAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Chemical","lcsb.mapviewer.model.map.reaction.Reaction"],"url":"http://humanmetabolism.org/"},{"name":"Entrez Gene","className":"lcsb.mapviewer.annotation.services.annotators.EntrezAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"http://www.ncbi.nlm.nih.gov/gene"},{"name":"Ensembl","className":"lcsb.mapviewer.annotation.services.annotators.EnsemblAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"www.ensembl.org"}],"options":[{"idObject":9,"type":"EMAIL_ADDRESS","value":"your.account@domain.com"},{"idObject":10,"type":"EMAIL_LOGIN","value":"your@login"},{"idObject":11,"type":"EMAIL_PASSWORD","value":"email.secret.password"},{"idObject":13,"type":"EMAIL_IMAP_SERVER","value":"your.imap.domain.com"},{"idObject":12,"type":"EMAIL_SMTP_SERVER","value":"your.smtp.domain.com"},{"idObject":14,"type":"EMAIL_SMTP_PORT","value":"25"},{"idObject":6,"type":"DEFAULT_MAP","value":"sample"},{"idObject":4,"type":"LOGO_IMG","value":"udl.png"},{"idObject":3,"type":"LOGO_LINK","value":"http://wwwen.uni.lu/"},{"idObject":7,"type":"SEARCH_DISTANCE","value":"10"},{"idObject":1,"type":"REQUEST_ACCOUNT_EMAIL","value":"your.email@domain.com"},{"idObject":8,"type":"SEARCH_RESULT_NUMBER","value":"100"},{"idObject":2,"type":"GOOGLE_ANALYTICS_IDENTIFIER","value":""},{"idObject":5,"type":"LOGO_TEXT","value":"University of Luxembourg"},{"idObject":56,"type":"X_FRAME_DOMAIN","value":"http://localhost:8080/"},{"idObject":131,"type":"BIG_FILE_STORAGE_DIR","value":"minerva-big/"},{"idObject":138,"type":"LEGEND_FILE_1","value":"resources/images/legend_a.png"},{"idObject":139,"type":"LEGEND_FILE_2","value":"resources/images/legend_b.png"},{"idObject":140,"type":"LEGEND_FILE_3","value":"resources/images/legend_c.png"},{"idObject":141,"type":"LEGEND_FILE_4","value":"resources/images/legend_d.png"},{"idObject":142,"type":"USER_MANUAL_FILE","value":"resources/other/user_guide.pdf"},{"idObject":205,"type":"MIN_COLOR_VAL","value":"FF0000"},{"idObject":206,"type":"MAX_COLOR_VAL","value":"fbff00"},{"idObject":218,"type":"SIMPLE_COLOR_VAL","value":"00FF00"}],"privilegeTypes":{"VIEW_PROJECT":{"commonName":"View project","valueType":"boolean","objectType":"Project"},"LAYOUT_MANAGEMENT":{"commonName":"Manage layouts","valueType":"boolean","objectType":"Project"},"PROJECT_MANAGEMENT":{"commonName":"Map management","valueType":"boolean","objectType":null},"CUSTOM_LAYOUTS":{"commonName":"Custom layouts","valueType":"int","objectType":null},"ADD_MAP":{"commonName":"Add project","valueType":"boolean","objectType":null},"LAYOUT_VIEW":{"commonName":"View layout","valueType":"boolean","objectType":"Layout"},"MANAGE_GENOMES":{"commonName":"Manage genomes","valueType":"boolean","objectType":null},"EDIT_COMMENTS_PROJECT":{"commonName":"Manage comments","valueType":"boolean","objectType":"Project"},"CONFIGURATION_MANAGE":{"commonName":"Manage configuration","valueType":"boolean","objectType":null},"USER_MANAGEMENT":{"commonName":"User management","valueType":"boolean","objectType":null}},"overlayTypes":[{"name":"GENERIC"},{"name":"GENETIC_VARIANT"}],"buildDate":"30/08/2017 16:58","reactionTypes":[{"name":"Unknown positive influence","className":"lcsb.mapviewer.model.map.reaction.type.UnknownPositiveInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Generic Reaction","className":"lcsb.mapviewer.model.map.reaction.Reaction","parentClass":"lcsb.mapviewer.model.map.BioEntity"},{"name":"Reduced physical stimulation","className":"lcsb.mapviewer.model.map.reaction.type.ReducedPhysicalStimulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Negative influence","className":"lcsb.mapviewer.model.map.reaction.type.NegativeInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Known transition omitted","className":"lcsb.mapviewer.model.map.reaction.type.KnownTransitionOmittedReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Reduced modulation","className":"lcsb.mapviewer.model.map.reaction.type.ReducedModulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Translation","className":"lcsb.mapviewer.model.map.reaction.type.TranslationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Heterodimer association","className":"lcsb.mapviewer.model.map.reaction.type.HeterodimerAssociationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Transcription","className":"lcsb.mapviewer.model.map.reaction.type.TranscriptionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced trigger","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedTriggerReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown negative influence","className":"lcsb.mapviewer.model.map.reaction.type.UnknownNegativeInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Truncation","className":"lcsb.mapviewer.model.map.reaction.type.TruncationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Transport","className":"lcsb.mapviewer.model.map.reaction.type.TransportReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Reduced trigger","className":"lcsb.mapviewer.model.map.reaction.type.ReducedTriggerReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"State transition","className":"lcsb.mapviewer.model.map.reaction.type.StateTransitionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Positive influence","className":"lcsb.mapviewer.model.map.reaction.type.PositiveInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced physical stimulation","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedPhysicalStimulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Boolean logic gate","className":"lcsb.mapviewer.model.map.reaction.type.BooleanLogicGateReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced modulation","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedModulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown transition","className":"lcsb.mapviewer.model.map.reaction.type.UnknownTransitionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Dissociation","className":"lcsb.mapviewer.model.map.reaction.type.DissociationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"}],"version":"11.0.0"}
\ No newline at end of file
+{"modelFormats":[{"handler":"lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser","extension":"xml","name":"CellDesigner SBML"},{"handler":"lcsb.mapviewer.converter.model.sbgnml.SbgnmlXmlConverter","extension":"sbgn","name":"SBGN-ML"}],"elementTypes":[{"name":"Degraded","className":"lcsb.mapviewer.model.map.species.Degraded","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.LeftSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.IonChannelProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.TopSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Ion","className":"lcsb.mapviewer.model.map.species.Ion","parentClass":"lcsb.mapviewer.model.map.species.Chemical"},{"name":"Species","className":"lcsb.mapviewer.model.map.species.Species","parentClass":"lcsb.mapviewer.model.map.species.Element"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.RightSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Drug","className":"lcsb.mapviewer.model.map.species.Drug","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.Protein","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.TruncatedProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.PathwayCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.BottomSquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"RNA","className":"lcsb.mapviewer.model.map.species.Rna","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Chemical","className":"lcsb.mapviewer.model.map.species.Chemical","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.Compartment","parentClass":"lcsb.mapviewer.model.map.species.Element"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.OvalCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Compartment","className":"lcsb.mapviewer.model.map.compartment.SquareCompartment","parentClass":"lcsb.mapviewer.model.map.compartment.Compartment"},{"name":"Unknown","className":"lcsb.mapviewer.model.map.species.Unknown","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Element","className":"lcsb.mapviewer.model.map.species.Element","parentClass":"lcsb.mapviewer.model.map.BioEntity"},{"name":"Phenotype","className":"lcsb.mapviewer.model.map.species.Phenotype","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Complex","className":"lcsb.mapviewer.model.map.species.Complex","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Antisense RNA","className":"lcsb.mapviewer.model.map.species.AntisenseRna","parentClass":"lcsb.mapviewer.model.map.species.Species"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.ReceptorProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Simple molecule","className":"lcsb.mapviewer.model.map.species.SimpleMolecule","parentClass":"lcsb.mapviewer.model.map.species.Chemical"},{"name":"Protein","className":"lcsb.mapviewer.model.map.species.GenericProtein","parentClass":"lcsb.mapviewer.model.map.species.Protein"},{"name":"Gene","className":"lcsb.mapviewer.model.map.species.Gene","parentClass":"lcsb.mapviewer.model.map.species.Species"}],"modificationStateTypes":{"PHOSPHORYLATED":{"commonName":"phosphorylated","abbreviation":"P"},"METHYLATED":{"commonName":"methylated","abbreviation":"Me"},"PALMYTOYLATED":{"commonName":"palmytoylated","abbreviation":"Pa"},"ACETYLATED":{"commonName":"acetylated","abbreviation":"Ac"},"SULFATED":{"commonName":"sulfated","abbreviation":"S"},"GLYCOSYLATED":{"commonName":"glycosylated","abbreviation":"G"},"PRENYLATED":{"commonName":"prenylated","abbreviation":"Pr"},"UBIQUITINATED":{"commonName":"ubiquitinated","abbreviation":"Ub"},"PROTONATED":{"commonName":"protonated","abbreviation":"H"},"HYDROXYLATED":{"commonName":"hydroxylated","abbreviation":"OH"},"MYRISTOYLATED":{"commonName":"myristoylated","abbreviation":"My"},"UNKNOWN":{"commonName":"unknown","abbreviation":"?"},"EMPTY":{"commonName":"empty","abbreviation":""},"DONT_CARE":{"commonName":"don't care","abbreviation":"*"}},"imageFormats":[{"handler":"lcsb.mapviewer.converter.graphics.PngImageGenerator","extension":"png","name":"PNG image"},{"handler":"lcsb.mapviewer.converter.graphics.PdfImageGenerator","extension":"pdf","name":"PDF"},{"handler":"lcsb.mapviewer.converter.graphics.SvgImageGenerator","extension":"svg","name":"SVG image"}],"annotators":[{"name":"Biocompendium","className":"lcsb.mapviewer.annotation.services.annotators.BiocompendiumAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Gene","lcsb.mapviewer.model.map.species.Rna"],"url":"http://biocompendium.embl.de/"},{"name":"Chebi","className":"lcsb.mapviewer.annotation.services.annotators.ChebiAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Chemical"],"url":"http://www.ebi.ac.uk/chebi/"},{"name":"Uniprot","className":"lcsb.mapviewer.annotation.services.annotators.UniprotAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Gene","lcsb.mapviewer.model.map.species.Rna"],"url":"http://www.uniprot.org/"},{"name":"Gene Ontology","className":"lcsb.mapviewer.annotation.services.annotators.GoAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Phenotype","lcsb.mapviewer.model.map.compartment.Compartment","lcsb.mapviewer.model.map.species.Complex"],"url":"http://amigo.geneontology.org/amigo"},{"name":"HGNC","className":"lcsb.mapviewer.annotation.services.annotators.HgncAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"http://www.genenames.org"},{"name":"Recon annotator","className":"lcsb.mapviewer.annotation.services.annotators.ReconAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Chemical","lcsb.mapviewer.model.map.reaction.Reaction"],"url":"http://humanmetabolism.org/"},{"name":"Entrez Gene","className":"lcsb.mapviewer.annotation.services.annotators.EntrezAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"http://www.ncbi.nlm.nih.gov/gene"},{"name":"Ensembl","className":"lcsb.mapviewer.annotation.services.annotators.EnsemblAnnotator","elementClassNames":["lcsb.mapviewer.model.map.species.Protein","lcsb.mapviewer.model.map.species.Rna","lcsb.mapviewer.model.map.species.Gene"],"url":"www.ensembl.org"}],"buildDate":"            28/09/2017 15:11","reactionTypes":[{"name":"Unknown positive influence","className":"lcsb.mapviewer.model.map.reaction.type.UnknownPositiveInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Generic Reaction","className":"lcsb.mapviewer.model.map.reaction.Reaction","parentClass":"lcsb.mapviewer.model.map.BioEntity"},{"name":"Reduced physical stimulation","className":"lcsb.mapviewer.model.map.reaction.type.ReducedPhysicalStimulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Negative influence","className":"lcsb.mapviewer.model.map.reaction.type.NegativeInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Known transition omitted","className":"lcsb.mapviewer.model.map.reaction.type.KnownTransitionOmittedReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Reduced modulation","className":"lcsb.mapviewer.model.map.reaction.type.ReducedModulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Translation","className":"lcsb.mapviewer.model.map.reaction.type.TranslationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Heterodimer association","className":"lcsb.mapviewer.model.map.reaction.type.HeterodimerAssociationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Transcription","className":"lcsb.mapviewer.model.map.reaction.type.TranscriptionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced trigger","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedTriggerReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown negative influence","className":"lcsb.mapviewer.model.map.reaction.type.UnknownNegativeInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Truncation","className":"lcsb.mapviewer.model.map.reaction.type.TruncationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Transport","className":"lcsb.mapviewer.model.map.reaction.type.TransportReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Reduced trigger","className":"lcsb.mapviewer.model.map.reaction.type.ReducedTriggerReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"State transition","className":"lcsb.mapviewer.model.map.reaction.type.StateTransitionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Positive influence","className":"lcsb.mapviewer.model.map.reaction.type.PositiveInfluenceReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced physical stimulation","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedPhysicalStimulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Boolean logic gate","className":"lcsb.mapviewer.model.map.reaction.type.BooleanLogicGateReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown reduced modulation","className":"lcsb.mapviewer.model.map.reaction.type.UnknownReducedModulationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Unknown transition","className":"lcsb.mapviewer.model.map.reaction.type.UnknownTransitionReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"},{"name":"Dissociation","className":"lcsb.mapviewer.model.map.reaction.type.DissociationReaction","parentClass":"lcsb.mapviewer.model.map.reaction.Reaction"}],"version":"11.0.1","mapTypes":[{"name":"Downstream targets","id":"DOWNSTREAM_TARGETS"},{"name":"Pathway","id":"PATHWAY"},{"name":"Unknown","id":"UNKNOWN"}],"miriamTypes":{"CHEMBL_TARGET":{"commonName":"ChEMBL target","uris":["urn:miriam:chembl.target"],"homepage":"https://www.ebi.ac.uk/chembldb/","registryIdentifier":"MIR:00000085"},"UNIPROT":{"commonName":"Uniprot","uris":["urn:miriam:uniprot"],"homepage":"http://www.uniprot.org/","registryIdentifier":"MIR:00000005"},"MI_R_BASE_MATURE_SEQUENCE":{"commonName":"miRBase Mature Sequence Database","uris":["urn:miriam:mirbase.mature"],"homepage":"http://www.mirbase.org/","registryIdentifier":"MIR:00000235"},"PFAM":{"commonName":"Protein Family Database","uris":["urn:miriam:pfam"],"homepage":"http://pfam.xfam.org//","registryIdentifier":"MIR:00000028"},"ENSEMBL_PLANTS":{"commonName":"Ensembl Plants","uris":["urn:miriam:ensembl.plant"],"homepage":"http://plants.ensembl.org/","registryIdentifier":"MIR:00000205"},"WIKIPEDIA":{"commonName":"Wikipedia (English)","uris":["urn:miriam:wikipedia.en"],"homepage":"http://en.wikipedia.org/wiki/Main_Page","registryIdentifier":"MIR:00000384"},"CHEBI":{"commonName":"Chebi","uris":["urn:miriam:obo.chebi","urn:miriam:chebi"],"homepage":"http://www.ebi.ac.uk/chebi/","registryIdentifier":"MIR:00000002"},"WIKIDATA":{"commonName":"Wikidata","uris":["urn:miriam:wikidata"],"homepage":"https://www.wikidata.org/","registryIdentifier":"MIR:00000549"},"REACTOME":{"commonName":"Reactome","uris":["urn:miriam:reactome"],"homepage":"http://www.reactome.org/","registryIdentifier":"MIR:00000018"},"EC":{"commonName":"Enzyme Nomenclature","uris":["urn:miriam:ec-code"],"homepage":"http://www.enzyme-database.org/","registryIdentifier":"MIR:00000004"},"UNIPROT_ISOFORM":{"commonName":"UniProt Isoform","uris":["urn:miriam:uniprot.isoform"],"homepage":"http://www.uniprot.org/","registryIdentifier":"MIR:00000388"},"OMIM":{"commonName":"Online Mendelian Inheritance in Man","uris":["urn:miriam:omim"],"homepage":"http://omim.org/","registryIdentifier":"MIR:00000016"},"DRUGBANK_TARGET_V4":{"commonName":"DrugBank Target v4","uris":["urn:miriam:drugbankv4.target"],"homepage":"http://www.drugbank.ca/targets","registryIdentifier":"MIR:00000528"},"MIR_TAR_BASE_MATURE_SEQUENCE":{"commonName":"miRTarBase Mature Sequence Database","uris":["urn:miriam:mirtarbase"],"homepage":"http://mirtarbase.mbc.nctu.edu.tw/","registryIdentifier":"MIR:00100739"},"CHEMBL_COMPOUND":{"commonName":"ChEMBL","uris":["urn:miriam:chembl.compound"],"homepage":"https://www.ebi.ac.uk/chembldb/","registryIdentifier":"MIR:00000084"},"KEGG_PATHWAY":{"commonName":"Kegg Pathway","uris":["urn:miriam:kegg.pathway"],"homepage":"http://www.genome.jp/kegg/pathway.html","registryIdentifier":"MIR:00000012"},"CAS":{"commonName":"Chemical Abstracts Service","uris":["urn:miriam:cas"],"homepage":"http://commonchemistry.org","registryIdentifier":"MIR:00000237"},"REFSEQ":{"commonName":"RefSeq","uris":["urn:miriam:refseq"],"homepage":"http://www.ncbi.nlm.nih.gov/projects/RefSeq/","registryIdentifier":"MIR:00000039"},"WORM_BASE":{"commonName":"WormBase","uris":["urn:miriam:wormbase"],"homepage":"http://wormbase.bio2rdf.org/fct","registryIdentifier":"MIR:00000027"},"MI_R_BASE_SEQUENCE":{"commonName":"miRBase Sequence Database","uris":["urn:miriam:mirbase"],"homepage":"http://www.mirbase.org/","registryIdentifier":"MIR:00000078"},"TAIR_LOCUS":{"commonName":"TAIR Locus","uris":["urn:miriam:tair.locus"],"homepage":"http://arabidopsis.org/index.jsp","registryIdentifier":"MIR:00000050"},"PHARM":{"commonName":"PharmGKB Pathways","uris":["urn:miriam:pharmgkb.pathways"],"homepage":"http://www.pharmgkb.org/","registryIdentifier":"MIR:00000089"},"PANTHER":{"commonName":"PANTHER Family","uris":["urn:miriam:panther.family","urn:miriam:panther"],"homepage":"http://www.pantherdb.org/","registryIdentifier":"MIR:00000060"},"TAXONOMY":{"commonName":"Taxonomy","uris":["urn:miriam:taxonomy"],"homepage":"http://www.ncbi.nlm.nih.gov/taxonomy/","registryIdentifier":"MIR:00000006"},"UNIGENE":{"commonName":"UniGene","uris":["urn:miriam:unigene"],"homepage":"http://www.ncbi.nlm.nih.gov/unigene","registryIdentifier":"MIR:00000346"},"HGNC":{"commonName":"HGNC","uris":["urn:miriam:hgnc"],"homepage":"http://www.genenames.org","registryIdentifier":"MIR:00000080"},"HGNC_SYMBOL":{"commonName":"HGNC Symbol","uris":["urn:miriam:hgnc.symbol"],"homepage":"http://www.genenames.org","registryIdentifier":"MIR:00000362"},"COG":{"commonName":"Clusters of Orthologous Groups","uris":["urn:miriam:cogs"],"homepage":"https://www.ncbi.nlm.nih.gov/COG/","registryIdentifier":"MIR:00000296"},"WIKIPATHWAYS":{"commonName":"WikiPathways","uris":["urn:miriam:wikipathways"],"homepage":"http://www.wikipathways.org/","registryIdentifier":"MIR:00000076"},"HMDB":{"commonName":"HMDB","uris":["urn:miriam:hmdb"],"homepage":"http://www.hmdb.ca/","registryIdentifier":"MIR:00000051"},"CHEMSPIDER":{"commonName":"ChemSpider","uris":["urn:miriam:chemspider"],"homepage":"http://www.chemspider.com//","registryIdentifier":"MIR:00000138"},"ENSEMBL":{"commonName":"Ensembl","uris":["urn:miriam:ensembl"],"homepage":"www.ensembl.org","registryIdentifier":"MIR:00000003"},"GO":{"commonName":"Gene Ontology","uris":["urn:miriam:obo.go","urn:miriam:go"],"homepage":"http://amigo.geneontology.org/amigo","registryIdentifier":"MIR:00000022"},"KEGG_REACTION":{"commonName":"Kegg Reaction","uris":["urn:miriam:kegg.reaction"],"homepage":"http://www.genome.jp/kegg/reaction/","registryIdentifier":"MIR:00000014"},"KEGG_ORTHOLOGY":{"commonName":"KEGG Orthology","uris":["urn:miriam:kegg.orthology"],"homepage":"http://www.genome.jp/kegg/ko.html","registryIdentifier":"MIR:00000116"},"PUBCHEM":{"commonName":"PubChem-compound","uris":["urn:miriam:pubchem.compound"],"homepage":"http://pubchem.ncbi.nlm.nih.gov/","registryIdentifier":"MIR:00000034"},"MESH_2012":{"commonName":"MeSH 2012","uris":["urn:miriam:mesh.2012","urn:miriam:mesh"],"homepage":"http://www.nlm.nih.gov/mesh/","registryIdentifier":"MIR:00000270"},"MGD":{"commonName":"Mouse Genome Database","uris":["urn:miriam:mgd"],"homepage":"http://www.informatics.jax.org/","registryIdentifier":"MIR:00000037"},"ENTREZ":{"commonName":"Entrez Gene","uris":["urn:miriam:ncbigene","urn:miriam:entrez.gene"],"homepage":"http://www.ncbi.nlm.nih.gov/gene","registryIdentifier":"MIR:00000069"},"PUBCHEM_SUBSTANCE":{"commonName":"PubChem-substance","uris":["urn:miriam:pubchem.substance"],"homepage":"http://pubchem.ncbi.nlm.nih.gov/","registryIdentifier":"MIR:00000033"},"CCDS":{"commonName":"Consensus CDS","uris":["urn:miriam:ccds"],"homepage":"http://www.ncbi.nlm.nih.gov/CCDS/","registryIdentifier":"MIR:00000375"},"KEGG_GENES":{"commonName":"Kegg Genes","uris":["urn:miriam:kegg.genes","urn:miriam:kegg.genes:hsa"],"homepage":"http://www.genome.jp/kegg/genes.html","registryIdentifier":"MIR:00000070"},"TOXICOGENOMIC_CHEMICAL":{"commonName":"Toxicogenomic Chemical","uris":["urn:miriam:ctd.chemical"],"homepage":"http://ctdbase.org/","registryIdentifier":"MIR:00000098"},"SGD":{"commonName":"Saccharomyces Genome Database","uris":["urn:miriam:sgd"],"homepage":"http://www.yeastgenome.org/","registryIdentifier":"MIR:00000023"},"KEGG_COMPOUND":{"commonName":"Kegg Compound","uris":["urn:miriam:kegg.compound"],"homepage":"http://www.genome.jp/kegg/ligand.html","registryIdentifier":"MIR:00000013"},"INTERPRO":{"commonName":"InterPro","uris":["urn:miriam:interpro"],"homepage":"http://www.ebi.ac.uk/interpro/","registryIdentifier":"MIR:00000011"},"UNKNOWN":{"commonName":"Unknown","uris":[],"homepage":null,"registryIdentifier":null},"DRUGBANK":{"commonName":"DrugBank","uris":["urn:miriam:drugbank"],"homepage":"http://www.drugbank.ca/","registryIdentifier":"MIR:00000102"},"PUBMED":{"commonName":"PubMed","uris":["urn:miriam:pubmed"],"homepage":"http://www.ncbi.nlm.nih.gov/PubMed/","registryIdentifier":"MIR:00000015"}},"options":[{"idObject":9,"type":"EMAIL_ADDRESS","value":"your.account@domain.com"},{"idObject":10,"type":"EMAIL_LOGIN","value":"your@login"},{"idObject":11,"type":"EMAIL_PASSWORD","value":"email.secret.password"},{"idObject":13,"type":"EMAIL_IMAP_SERVER","value":"your.imap.domain.com"},{"idObject":12,"type":"EMAIL_SMTP_SERVER","value":"your.smtp.domain.com"},{"idObject":14,"type":"EMAIL_SMTP_PORT","value":"25"},{"idObject":6,"type":"DEFAULT_MAP","value":"sample"},{"idObject":4,"type":"LOGO_IMG","value":"udl.png"},{"idObject":3,"type":"LOGO_LINK","value":"http://wwwen.uni.lu/"},{"idObject":7,"type":"SEARCH_DISTANCE","value":"10"},{"idObject":1,"type":"REQUEST_ACCOUNT_EMAIL","value":"your.email@domain.com"},{"idObject":8,"type":"SEARCH_RESULT_NUMBER","value":"100"},{"idObject":2,"type":"GOOGLE_ANALYTICS_IDENTIFIER","value":""},{"idObject":5,"type":"LOGO_TEXT","value":"University of Luxembourg"},{"idObject":56,"type":"X_FRAME_DOMAIN","value":"http://localhost:8080/"},{"idObject":131,"type":"BIG_FILE_STORAGE_DIR","value":"minerva-big/"},{"idObject":138,"type":"LEGEND_FILE_1","value":"resources/images/legend_a.png"},{"idObject":139,"type":"LEGEND_FILE_2","value":"resources/images/legend_b.png"},{"idObject":140,"type":"LEGEND_FILE_3","value":"resources/images/legend_c.png"},{"idObject":141,"type":"LEGEND_FILE_4","value":"resources/images/legend_d.png"},{"idObject":142,"type":"USER_MANUAL_FILE","value":"resources/other/user_guide.pdf"},{"idObject":205,"type":"MIN_COLOR_VAL","value":"FF0000"},{"idObject":206,"type":"MAX_COLOR_VAL","value":"fbff00"},{"idObject":218,"type":"SIMPLE_COLOR_VAL","value":"00FF00"}],"privilegeTypes":{"VIEW_PROJECT":{"commonName":"View project","valueType":"boolean","objectType":"Project"},"LAYOUT_MANAGEMENT":{"commonName":"Manage layouts","valueType":"boolean","objectType":"Project"},"PROJECT_MANAGEMENT":{"commonName":"Map management","valueType":"boolean","objectType":null},"CUSTOM_LAYOUTS":{"commonName":"Custom layouts","valueType":"int","objectType":null},"ADD_MAP":{"commonName":"Add project","valueType":"boolean","objectType":null},"LAYOUT_VIEW":{"commonName":"View layout","valueType":"boolean","objectType":"Layout"},"MANAGE_GENOMES":{"commonName":"Manage genomes","valueType":"boolean","objectType":null},"EDIT_COMMENTS_PROJECT":{"commonName":"Manage comments","valueType":"boolean","objectType":"Project"},"CONFIGURATION_MANAGE":{"commonName":"Manage configuration","valueType":"boolean","objectType":null},"USER_MANAGEMENT":{"commonName":"User management","valueType":"boolean","objectType":null}},"overlayTypes":[{"name":"GENERIC"},{"name":"GENETIC_VARIANT"}]}
\ No newline at end of file
diff --git a/frontend-js/testFiles/map/complex_model_with_overlays.zip b/frontend-js/testFiles/map/complex_model_with_overlays.zip
new file mode 100644
index 0000000000000000000000000000000000000000..0aa92bc0ab6aa5a047d64677c3520d45de75e878
Binary files /dev/null and b/frontend-js/testFiles/map/complex_model_with_overlays.zip differ
diff --git a/frontend-js/testFiles/map/complex_model_with_submaps.zip b/frontend-js/testFiles/map/complex_model_with_submaps.zip
new file mode 100644
index 0000000000000000000000000000000000000000..5985c849bbeff096451d99b43a430ec6c266cf5b
Binary files /dev/null and b/frontend-js/testFiles/map/complex_model_with_submaps.zip differ
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java
index e1ca1c0399715a380efb489f415821ebd4835974..52d86daa0f012301a2d1813ef17b167af9d1be65 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java
@@ -19,68 +19,71 @@ import lcsb.mapviewer.services.interfaces.IConfigurationService;
 
 @RestController
 public class ConfigurationController extends BaseController {
-	@Autowired
-	private ConfigurationRestImpl	configurationController;
+  @Autowired
+  private ConfigurationRestImpl configurationController;
 
-	@Autowired
-	private IConfigurationService	configurationService;
+  @Autowired
+  private IConfigurationService configurationService;
 
-	/**
-	 * Context used to determine deployment path.
-	 */
-	@Autowired
-	private ServletContext				context;
+  /**
+   * Context used to determine deployment path.
+   */
+  @Autowired
+  private ServletContext context;
 
-	@RequestMapping(value = "/configuration/", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Map<String, Object> getOverlayTypes(@CookieValue(value = Configuration.AUTH_TOKEN) String token) throws SecurityException {
-		Map<String, Object> result = new HashMap<>();
-		result.put("options", configurationController.getAllValues(token));
-		result.put("imageFormats", configurationController.getImageFormats(token));
-		result.put("modelFormats", configurationController.getModelFormats(token));
-		result.put("overlayTypes", configurationController.getOverlayTypes(token));
-		result.put("elementTypes", configurationController.getElementTypes(token));
-		result.put("reactionTypes", configurationController.getReactionTypes(token));
-		result.put("miriamTypes", configurationController.getMiriamTypes(token));
-		result.put("modificationStateTypes", configurationController.getModificationStateTypes(token));
-		result.put("privilegeTypes", configurationController.getPrivilegeTypes(token));
-		result.put("version", configurationService.getSystemSvnVersion(context.getRealPath("/")));
-		result.put("buildDate", configurationService.getSystemBuild(context.getRealPath("/")));
-		result.put("annotators", configurationController.getAnnotators(token));
-		return result;
-	}
+  @RequestMapping(value = "/configuration/", method = { RequestMethod.GET }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public Map<String, Object> getOverlayTypes(@CookieValue(value = Configuration.AUTH_TOKEN) String token)
+      throws SecurityException {
+    Map<String, Object> result = new HashMap<>();
+    result.put("options", configurationController.getAllValues(token));
+    result.put("imageFormats", configurationController.getImageFormats(token));
+    result.put("modelFormats", configurationController.getModelFormats(token));
+    result.put("overlayTypes", configurationController.getOverlayTypes(token));
+    result.put("elementTypes", configurationController.getElementTypes(token));
+    result.put("reactionTypes", configurationController.getReactionTypes(token));
+    result.put("miriamTypes", configurationController.getMiriamTypes(token));
+    result.put("mapTypes", configurationController.getMapTypes(token));
+    result.put("modificationStateTypes", configurationController.getModificationStateTypes(token));
+    result.put("privilegeTypes", configurationController.getPrivilegeTypes(token));
+    result.put("version", configurationService.getSystemSvnVersion(context.getRealPath("/")));
+    result.put("buildDate", configurationService.getSystemBuild(context.getRealPath("/")));
+    result.put("annotators", configurationController.getAnnotators(token));
+    return result;
+  }
 
-	/**
-	 * @return the configurationController
-	 * @see #configurationController
-	 */
-	public ConfigurationRestImpl getConfigurationController() {
-		return configurationController;
-	}
+  /**
+   * @return the configurationController
+   * @see #configurationController
+   */
+  public ConfigurationRestImpl getConfigurationController() {
+    return configurationController;
+  }
 
-	/**
-	 * @param configurationController
-	 *          the configurationController to set
-	 * @see #configurationController
-	 */
-	public void setConfigurationController(ConfigurationRestImpl configurationController) {
-		this.configurationController = configurationController;
-	}
+  /**
+   * @param configurationController
+   *          the configurationController to set
+   * @see #configurationController
+   */
+  public void setConfigurationController(ConfigurationRestImpl configurationController) {
+    this.configurationController = configurationController;
+  }
 
-	/**
-	 * @return the configurationService
-	 * @see #configurationService
-	 */
-	public IConfigurationService getConfigurationService() {
-		return configurationService;
-	}
+  /**
+   * @return the configurationService
+   * @see #configurationService
+   */
+  public IConfigurationService getConfigurationService() {
+    return configurationService;
+  }
 
-	/**
-	 * @param configurationService
-	 *          the configurationService to set
-	 * @see #configurationService
-	 */
-	public void setConfigurationService(IConfigurationService configurationService) {
-		this.configurationService = configurationService;
-	}
+  /**
+   * @param configurationService
+   *          the configurationService to set
+   * @see #configurationService
+   */
+  public void setConfigurationService(IConfigurationService configurationService) {
+    this.configurationService = configurationService;
+  }
 
 }
\ No newline at end of file
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java
index 6eb4066d9b08d66ee6a655414166bef47565e3bf..effd2cb87d76612dfeb95ec991946e8d0c174de3 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java
@@ -25,6 +25,8 @@ import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser;
 import lcsb.mapviewer.converter.model.sbgnml.SbgnmlXmlConverter;
 import lcsb.mapviewer.model.map.BioEntity;
 import lcsb.mapviewer.model.map.MiriamType;
+import lcsb.mapviewer.model.map.model.ModelSubmodelConnection;
+import lcsb.mapviewer.model.map.model.SubmodelType;
 import lcsb.mapviewer.model.map.reaction.Reaction;
 import lcsb.mapviewer.model.map.species.Element;
 import lcsb.mapviewer.model.map.species.field.ModificationState;
@@ -39,216 +41,228 @@ import lcsb.mapviewer.services.view.ConfigurationView;
 
 @Transactional(value = "txManager")
 public class ConfigurationRestImpl {
-	/**
-	 * Default class logger.
-	 */
-	@SuppressWarnings("unused")
-	private Logger								logger = Logger.getLogger(ConfigurationRestImpl.class);
-
-	@Autowired
-	private IUserService					userService;
-
-	@Autowired
-	private IConfigurationService	configurationService;
-
-	@Autowired
-	private ModelAnnotator				modelAnnotator;
-
-	public List<ConfigurationView> getAllValues(String token) throws SecurityException {
-		userService.getToken(token);
-		return configurationService.getAllValues();
-	}
-
-	/**
-	 * @return the userService
-	 * @see #userService
-	 */
-	public IUserService getUserService() {
-		return userService;
-	}
-
-	/**
-	 * @param userService
-	 *          the userService to set
-	 * @see #userService
-	 */
-	public void setUserService(IUserService userService) {
-		this.userService = userService;
-	}
-
-	/**
-	 * @return the configurationService
-	 * @see #configurationService
-	 */
-	public IConfigurationService getConfigurationService() {
-		return configurationService;
-	}
-
-	/**
-	 * @param configurationService
-	 *          the configurationService to set
-	 * @see #configurationService
-	 */
-	public void setConfigurationService(IConfigurationService configurationService) {
-		this.configurationService = configurationService;
-	}
-
-	public List<Map<String, Object>> getImageFormats(String token) throws SecurityException {
-		userService.getToken(token);
-
-		List<Map<String, Object>> result = new ArrayList<>();
-		ImageGenerators imageGenerators = new ImageGenerators();
-		List<Pair<String, Class<? extends AbstractImageGenerator>>> imageGeneratorList = imageGenerators.getAvailableImageGenerators();
-
-		for (Pair<String, Class<? extends AbstractImageGenerator>> element : imageGeneratorList) {
-			Map<String, Object> row = new HashMap<>();
-			row.put("name", element.getLeft());
-			row.put("handler", element.getRight().getCanonicalName());
-			row.put("extension", imageGenerators.getExtension(element.getRight()));
-			result.add(row);
-		}
-		return result;
-	}
-
-	public List<Map<String, Object>> getModelFormats(String token) throws SecurityException {
-		userService.getToken(token);
-		List<IConverter> converters = new ArrayList<>();
-		converters.add(new CellDesignerXmlParser());
-		converters.add(new SbgnmlXmlConverter());
-
-		List<Map<String, Object>> result = new ArrayList<>();
-
-		for (IConverter converter : converters) {
-			Map<String, Object> row = new HashMap<>();
-			row.put("name", converter.getCommonName());
-			row.put("handler", converter.getClass().getCanonicalName());
-			row.put("extension", converter.getFileExtension());
-			result.add(row);
-		}
-		return result;
-	}
-
-	public List<Map<String, Object>> getOverlayTypes(String token) throws SecurityException {
-		userService.getToken(token);
-		List<Map<String, Object>> result = new ArrayList<>();
-		for (ColorSchemaType type : ColorSchemaType.values()) {
-			Map<String, Object> map = new HashMap<>();
-			map.put("name", type.name());
-			result.add(map);
-		}
-		return result;
-	}
-
-	public Set<Map<String, String>> getElementTypes(String token) throws SecurityException {
-		userService.getToken(token);
-
-		return getClassStringTypesList(Element.class);
-	}
-
-	private Set<Map<String, String>> getClassStringTypesList(Class<?> elementClass) {
-		Set<Map<String, String>> result = new HashSet<>();
-		ElementUtils elementUtils = new ElementUtils();
-		ClassTreeNode top = elementUtils.getAnnotatedElementClassTree();
-		Queue<ClassTreeNode> queue = new LinkedList<>();
-		queue.add(top);
-		while (!queue.isEmpty()) {
-			ClassTreeNode clazz = queue.poll();
-			for (ClassTreeNode child : clazz.getChildren()) {
-				queue.add(child);
-			}
-			if (elementClass.isAssignableFrom(clazz.getClazz())) {
-				Map<String, String> row = new HashMap<>();
-				row.put("className", clazz.getClazz().getName());
-				row.put("name", clazz.getCommonName());
-				if (clazz.getParent() == null) {
-					row.put("parentClass", null);
-				} else {
-					row.put("parentClass", clazz.getParent().getClazz().getName());
-				}
-				result.add(row);
-			}
-		}
-		return result;
-	}
-
-	public Set<Map<String, String>> getReactionTypes(String token) throws SecurityException {
-		userService.getToken(token);
-
-		return getClassStringTypesList(Reaction.class);
-	}
-
-	public Map<String, Object> getMiriamTypes(String id) {
-		Map<String, Object> result = new HashMap<>();
-		for (MiriamType type : MiriamType.values()) {
-			result.put(type.name(), createMiriamTypeResponse(type));
-		}
-		return result;
-	}
-
-	private Map<String, Object> createMiriamTypeResponse(MiriamType type) {
-		Map<String, Object> result = new HashMap<>();
-		result.put("commonName", type.getCommonName());
-		result.put("homepage", type.getDbHomepage());
-		result.put("registryIdentifier", type.getRegistryIdentifier());
-		result.put("uris", type.getUris());
-
-		return result;
-	}
-
-	public Object getModificationStateTypes(String token) {
-		Map<String, Object> result = new HashMap<>();
-		for (ModificationState type : ModificationState.values()) {
-			result.put(type.name(), createModificationStateResponse(type));
-		}
-		return result;
-	}
-
-	private Map<String, Object> createModificationStateResponse(ModificationState type) {
-		Map<String, Object> result = new HashMap<>();
-		result.put("commonName", type.getFullName());
-		result.put("abbreviation", type.getAbbreviation());
-		return result;
-	}
-
-	public Map<String, Object> getPrivilegeTypes(String id) {
-		Map<String, Object> result = new HashMap<>();
-		for (PrivilegeType type : PrivilegeType.values()) {
-			result.put(type.name(), createPrivilegeTypeResponse(type));
-		}
-		return result;
-	}
-
-	private Map<String, Object> createPrivilegeTypeResponse(PrivilegeType type) {
-		Map<String, Object> result = new HashMap<>();
-		result.put("commonName", type.getCommonName());
-		if (type.getPrivilegeObjectType() != null) {
-			result.put("objectType", type.getPrivilegeObjectType().getSimpleName());
-		} else {
-			result.put("objectType", null);
-		}
-		if (type.isNumeric()) {
-			result.put("valueType", "int");
-		} else {
-			result.put("valueType", "boolean");
-		}
-		return result;
-	}
-
-	public List<Map<String, Object>> getAnnotators(String token) {
-		List<Map<String, Object>> result = new ArrayList<>();
-		for (ElementAnnotator annotator : modelAnnotator.getAvailableAnnotators()) {
-			result.add(prepareAnnotator(annotator));
-		}
-		return result;
-	}
-
-	private Map<String, Object> prepareAnnotator(ElementAnnotator annotator) {
-		Map<String, Object> result = new HashMap<>();
-		result.put("className", annotator.getClass().getName());
-		result.put("name", annotator.getCommonName());
-		result.put("url", annotator.getUrl());
-		result.put("elementClassNames", annotator.getValidClasses());
-		return result;
-	}
+  /**
+   * Default class logger.
+   */
+  @SuppressWarnings("unused")
+  private Logger logger = Logger.getLogger(ConfigurationRestImpl.class);
+
+  @Autowired
+  private IUserService userService;
+
+  @Autowired
+  private IConfigurationService configurationService;
+
+  @Autowired
+  private ModelAnnotator modelAnnotator;
+
+  public List<ConfigurationView> getAllValues(String token) throws SecurityException {
+    userService.getToken(token);
+    return configurationService.getAllValues();
+  }
+
+  /**
+   * @return the userService
+   * @see #userService
+   */
+  public IUserService getUserService() {
+    return userService;
+  }
+
+  /**
+   * @param userService
+   *          the userService to set
+   * @see #userService
+   */
+  public void setUserService(IUserService userService) {
+    this.userService = userService;
+  }
+
+  /**
+   * @return the configurationService
+   * @see #configurationService
+   */
+  public IConfigurationService getConfigurationService() {
+    return configurationService;
+  }
+
+  /**
+   * @param configurationService
+   *          the configurationService to set
+   * @see #configurationService
+   */
+  public void setConfigurationService(IConfigurationService configurationService) {
+    this.configurationService = configurationService;
+  }
+
+  public List<Map<String, Object>> getImageFormats(String token) throws SecurityException {
+    userService.getToken(token);
+
+    List<Map<String, Object>> result = new ArrayList<>();
+    ImageGenerators imageGenerators = new ImageGenerators();
+    List<Pair<String, Class<? extends AbstractImageGenerator>>> imageGeneratorList = imageGenerators
+        .getAvailableImageGenerators();
+
+    for (Pair<String, Class<? extends AbstractImageGenerator>> element : imageGeneratorList) {
+      Map<String, Object> row = new HashMap<>();
+      row.put("name", element.getLeft());
+      row.put("handler", element.getRight().getCanonicalName());
+      row.put("extension", imageGenerators.getExtension(element.getRight()));
+      result.add(row);
+    }
+    return result;
+  }
+
+  public List<Map<String, Object>> getModelFormats(String token) throws SecurityException {
+    userService.getToken(token);
+    List<IConverter> converters = new ArrayList<>();
+    converters.add(new CellDesignerXmlParser());
+    converters.add(new SbgnmlXmlConverter());
+
+    List<Map<String, Object>> result = new ArrayList<>();
+
+    for (IConverter converter : converters) {
+      Map<String, Object> row = new HashMap<>();
+      row.put("name", converter.getCommonName());
+      row.put("handler", converter.getClass().getCanonicalName());
+      row.put("extension", converter.getFileExtension());
+      result.add(row);
+    }
+    return result;
+  }
+
+  public List<Map<String, Object>> getOverlayTypes(String token) throws SecurityException {
+    userService.getToken(token);
+    List<Map<String, Object>> result = new ArrayList<>();
+    for (ColorSchemaType type : ColorSchemaType.values()) {
+      Map<String, Object> map = new HashMap<>();
+      map.put("name", type.name());
+      result.add(map);
+    }
+    return result;
+  }
+
+  public Set<Map<String, String>> getElementTypes(String token) throws SecurityException {
+    userService.getToken(token);
+
+    return getClassStringTypesList(Element.class);
+  }
+
+  private Set<Map<String, String>> getClassStringTypesList(Class<?> elementClass) {
+    Set<Map<String, String>> result = new HashSet<>();
+    ElementUtils elementUtils = new ElementUtils();
+    ClassTreeNode top = elementUtils.getAnnotatedElementClassTree();
+    Queue<ClassTreeNode> queue = new LinkedList<>();
+    queue.add(top);
+    while (!queue.isEmpty()) {
+      ClassTreeNode clazz = queue.poll();
+      for (ClassTreeNode child : clazz.getChildren()) {
+        queue.add(child);
+      }
+      if (elementClass.isAssignableFrom(clazz.getClazz())) {
+        Map<String, String> row = new HashMap<>();
+        row.put("className", clazz.getClazz().getName());
+        row.put("name", clazz.getCommonName());
+        if (clazz.getParent() == null) {
+          row.put("parentClass", null);
+        } else {
+          row.put("parentClass", clazz.getParent().getClazz().getName());
+        }
+        result.add(row);
+      }
+    }
+    return result;
+  }
+
+  public Set<Map<String, String>> getReactionTypes(String token) throws SecurityException {
+    userService.getToken(token);
+
+    return getClassStringTypesList(Reaction.class);
+  }
+
+  public Map<String, Object> getMiriamTypes(String id) {
+    Map<String, Object> result = new HashMap<>();
+    for (MiriamType type : MiriamType.values()) {
+      result.put(type.name(), createMiriamTypeResponse(type));
+    }
+    return result;
+  }
+
+  private Map<String, Object> createMiriamTypeResponse(MiriamType type) {
+    Map<String, Object> result = new HashMap<>();
+    result.put("commonName", type.getCommonName());
+    result.put("homepage", type.getDbHomepage());
+    result.put("registryIdentifier", type.getRegistryIdentifier());
+    result.put("uris", type.getUris());
+
+    return result;
+  }
+
+  public Object getModificationStateTypes(String token) {
+    Map<String, Object> result = new HashMap<>();
+    for (ModificationState type : ModificationState.values()) {
+      result.put(type.name(), createModificationStateResponse(type));
+    }
+    return result;
+  }
+
+  private Map<String, Object> createModificationStateResponse(ModificationState type) {
+    Map<String, Object> result = new HashMap<>();
+    result.put("commonName", type.getFullName());
+    result.put("abbreviation", type.getAbbreviation());
+    return result;
+  }
+
+  public Map<String, Object> getPrivilegeTypes(String id) {
+    Map<String, Object> result = new HashMap<>();
+    for (PrivilegeType type : PrivilegeType.values()) {
+      result.put(type.name(), createPrivilegeTypeResponse(type));
+    }
+    return result;
+  }
+
+  private Map<String, Object> createPrivilegeTypeResponse(PrivilegeType type) {
+    Map<String, Object> result = new HashMap<>();
+    result.put("commonName", type.getCommonName());
+    if (type.getPrivilegeObjectType() != null) {
+      result.put("objectType", type.getPrivilegeObjectType().getSimpleName());
+    } else {
+      result.put("objectType", null);
+    }
+    if (type.isNumeric()) {
+      result.put("valueType", "int");
+    } else {
+      result.put("valueType", "boolean");
+    }
+    return result;
+  }
+
+  public List<Map<String, Object>> getAnnotators(String token) {
+    List<Map<String, Object>> result = new ArrayList<>();
+    for (ElementAnnotator annotator : modelAnnotator.getAvailableAnnotators()) {
+      result.add(prepareAnnotator(annotator));
+    }
+    return result;
+  }
+
+  private Map<String, Object> prepareAnnotator(ElementAnnotator annotator) {
+    Map<String, Object> result = new HashMap<>();
+    result.put("className", annotator.getClass().getName());
+    result.put("name", annotator.getCommonName());
+    result.put("url", annotator.getUrl());
+    result.put("elementClassNames", annotator.getValidClasses());
+    return result;
+  }
+
+  public List<Map<String, Object>> getMapTypes(String token) {
+    List<Map<String, Object>> result = new ArrayList<>();
+    for (SubmodelType type : SubmodelType.values()) {
+      Map<String, Object> row = new HashMap<>();
+      row.put("id", type.name());
+      row.put("name", type.getCommonName());
+      result.add(row);
+    }
+    return result;
+  }
 
 }
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java
index 793084e98a0c168646a689fe89ae948158d3c591..3c2db6e9d4f9186d3ddc9b1c29cadf0ef368f9c3 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java
@@ -33,141 +33,152 @@ import lcsb.mapviewer.services.SecurityException;
 
 @RestController
 public class ProjectController extends BaseController {
-	private Logger					logger = Logger.getLogger(ProjectController.class);
-
-	@Autowired
-	private ServletContext	context;
-
-	@Autowired
-	private ProjectRestImpl	projectController;
-
-	@RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ProjectMetaData getProject(//
-			@PathVariable(value = "projectId") String projectId, //
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, ObjectNotFoundException {
-		return projectController.getProject(projectId, token);
-	}
-
-	@RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.PATCH }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ProjectMetaData updateProject(//
-			@RequestBody String body, //
-			@PathVariable(value = "projectId") String projectId, //
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, IOException, QueryException {
-		Map<String, Object> node = parseBody(body);
-		Map<String, Object> data = getData(node, "project");
-		return projectController.updateProject(token, projectId, data);
-
-	}
-
-	@RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ProjectMetaData addProject(//
-			@RequestBody MultiValueMap<String,String> formData, //
-			@PathVariable(value = "projectId") String projectId, //
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, IOException, QueryException {
-		return projectController.addProject(token, projectId, formData, context.getRealPath("/"));
-
-	}
-
-	@RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ProjectMetaData removeProject(//
-			@PathVariable(value = "projectId") String projectId, //
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, IOException, QueryException {
-		return projectController.removeProject(token, projectId, context.getRealPath("/"));
-
-	}
-
-	@RequestMapping(value = "/projects/", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public List<ProjectMetaData> getProjects(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, ObjectNotFoundException {
-		return projectController.getProjects(token);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/statistics", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Object getStatistics(//
-			@PathVariable(value = "projectId") String projectId, //
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token //
-	) throws SecurityException, ObjectNotFoundException {
-		return projectController.getStatistics(projectId, token);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}:downloadSource", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ResponseEntity<byte[]> getProjectSource(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId //
-	) throws SecurityException, QueryException {
-
-		FileEntry file = projectController.getSource(token, projectId);
-		MediaType type = MediaType.TEXT_PLAIN;
-		if (file.getOriginalFileName().endsWith("xml")) {
-			type = MediaType.APPLICATION_XML;
-		} else if (file.getOriginalFileName().endsWith("zip")) {
-			type = MediaType.APPLICATION_OCTET_STREAM;
-		}
-		return ResponseEntity
-				.ok().contentLength(file.getFileContent().length).contentType(type).header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
-				.body(file.getFileContent());
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadImage", method = { RequestMethod.GET },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ResponseEntity<byte[]> getModelAsImage(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@PathVariable(value = "modelId") String modelId, //
-			@RequestParam(value = "handlerClass") String handlerClass, //
-			@RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, //
-			@RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, //
-			@RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, //
-			@RequestParam(value = "polygonString", defaultValue = "") String polygonString//
-	) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, DrawingException {
-
-		FileEntry file = projectController.getModelAsImage(token, projectId, modelId, handlerClass, backgroundOverlayId, overlayIds, zoomLevel, polygonString);
-		MediaType type = MediaType.APPLICATION_OCTET_STREAM;
-		return ResponseEntity
-				.ok().contentLength(file.getFileContent().length).contentType(type).header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
-				.body(file.getFileContent());
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadModel", method = { RequestMethod.GET },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public ResponseEntity<byte[]> getModelAsModelFile(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@PathVariable(value = "modelId") String modelId, //
-			@RequestParam(value = "handlerClass") String handlerClass, //
-			@RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, //
-			@RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, //
-			@RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, //
-			@RequestParam(value = "polygonString", defaultValue = "") String polygonString//
-	) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, ConverterException,
-			InconsistentModelException {
-
-		FileEntry file = projectController.getModelAsModelFile(token, projectId, modelId, handlerClass, backgroundOverlayId, overlayIds, zoomLevel, polygonString);
-		MediaType type = MediaType.APPLICATION_OCTET_STREAM;
-		return ResponseEntity
-				.ok().contentLength(file.getFileContent().length).contentType(type).header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
-				.body(file.getFileContent());
-	}
-
-	/**
-	 * @return the context
-	 * @see #context
-	 */
-	public ServletContext getContext() {
-		return context;
-	}
-
-	/**
-	 * @param context the context to set
-	 * @see #context
-	 */
-	public void setContext(ServletContext context) {
-		this.context = context;
-	}
+  @SuppressWarnings("unused")
+  private Logger logger = Logger.getLogger(ProjectController.class);
+
+  @Autowired
+  private ServletContext context;
+
+  @Autowired
+  private ProjectRestImpl projectController;
+
+  @RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.GET }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public ProjectMetaData getProject(//
+      @PathVariable(value = "projectId") String projectId, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, ObjectNotFoundException {
+    return projectController.getProject(projectId, token);
+  }
+
+  @RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.PATCH }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public ProjectMetaData updateProject(//
+      @RequestBody String body, //
+      @PathVariable(value = "projectId") String projectId, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, IOException, QueryException {
+    Map<String, Object> node = parseBody(body);
+    Map<String, Object> data = getData(node, "project");
+    return projectController.updateProject(token, projectId, data);
+
+  }
+
+  @RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.POST }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public ProjectMetaData addProject(//
+      @RequestBody MultiValueMap<String, Object> formData, //
+      @PathVariable(value = "projectId") String projectId, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, IOException, QueryException {
+    return projectController.addProject(token, projectId, formData, context.getRealPath("/"));
+
+  }
+
+  @RequestMapping(value = "/projects/{projectId:.+}", method = { RequestMethod.DELETE }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public ProjectMetaData removeProject(//
+      @PathVariable(value = "projectId") String projectId, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, IOException, QueryException {
+    return projectController.removeProject(token, projectId, context.getRealPath("/"));
+
+  }
+
+  @RequestMapping(value = "/projects/", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public List<ProjectMetaData> getProjects(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, ObjectNotFoundException {
+    return projectController.getProjects(token);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/statistics", method = { RequestMethod.GET }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public Object getStatistics(//
+      @PathVariable(value = "projectId") String projectId, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token //
+  ) throws SecurityException, ObjectNotFoundException {
+    return projectController.getStatistics(projectId, token);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}:downloadSource", method = { RequestMethod.GET }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public ResponseEntity<byte[]> getProjectSource(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId //
+  ) throws SecurityException, QueryException {
+
+    FileEntry file = projectController.getSource(token, projectId);
+    MediaType type = MediaType.TEXT_PLAIN;
+    if (file.getOriginalFileName().endsWith("xml")) {
+      type = MediaType.APPLICATION_XML;
+    } else if (file.getOriginalFileName().endsWith("zip")) {
+      type = MediaType.APPLICATION_OCTET_STREAM;
+    }
+    return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type)
+        .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
+        .body(file.getFileContent());
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadImage", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public ResponseEntity<byte[]> getModelAsImage(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @PathVariable(value = "modelId") String modelId, //
+      @RequestParam(value = "handlerClass") String handlerClass, //
+      @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, //
+      @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, //
+      @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, //
+      @RequestParam(value = "polygonString", defaultValue = "") String polygonString//
+  ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException,
+      DrawingException {
+
+    FileEntry file = projectController.getModelAsImage(token, projectId, modelId, handlerClass, backgroundOverlayId,
+        overlayIds, zoomLevel, polygonString);
+    MediaType type = MediaType.APPLICATION_OCTET_STREAM;
+    return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type)
+        .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
+        .body(file.getFileContent());
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadModel", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public ResponseEntity<byte[]> getModelAsModelFile(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @PathVariable(value = "modelId") String modelId, //
+      @RequestParam(value = "handlerClass") String handlerClass, //
+      @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, //
+      @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, //
+      @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, //
+      @RequestParam(value = "polygonString", defaultValue = "") String polygonString//
+  ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException,
+      ConverterException, InconsistentModelException {
+
+    FileEntry file = projectController.getModelAsModelFile(token, projectId, modelId, handlerClass, backgroundOverlayId,
+        overlayIds, zoomLevel, polygonString);
+    MediaType type = MediaType.APPLICATION_OCTET_STREAM;
+    return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type)
+        .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
+        .body(file.getFileContent());
+  }
+
+  /**
+   * @return the context
+   * @see #context
+   */
+  public ServletContext getContext() {
+    return context;
+  }
+
+  /**
+   * @param context
+   *          the context to set
+   * @see #context
+   */
+  public void setContext(ServletContext context) {
+    this.context = context;
+  }
 
 }
\ No newline at end of file
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java
index b788a8feb8c46cccf085cc3aacc6986f40341f21..6a50de4c1f70aa97abae624754c51e4b116e9f6a 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java
@@ -38,9 +38,14 @@ import lcsb.mapviewer.commands.SetFixedHierarchyLevelCommand;
 import lcsb.mapviewer.commands.SubModelCommand;
 import lcsb.mapviewer.common.Configuration;
 import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.common.exception.NotImplementedException;
 import lcsb.mapviewer.converter.ConverterException;
 import lcsb.mapviewer.converter.IConverter;
 import lcsb.mapviewer.converter.graphics.AbstractImageGenerator.Params;
+import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
+import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
+import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
+import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.converter.graphics.DrawingException;
 import lcsb.mapviewer.converter.graphics.ImageGenerators;
 import lcsb.mapviewer.model.Project;
@@ -56,6 +61,7 @@ import lcsb.mapviewer.model.map.layout.ColorSchema;
 import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException;
 import lcsb.mapviewer.model.map.layout.Layout;
 import lcsb.mapviewer.model.map.model.Model;
+import lcsb.mapviewer.model.map.model.SubmodelType;
 import lcsb.mapviewer.model.map.reaction.Reaction;
 import lcsb.mapviewer.model.map.species.Element;
 import lcsb.mapviewer.model.user.PrivilegeType;
@@ -75,7 +81,7 @@ public class ProjectRestImpl extends BaseRestImpl {
 
   /**
    * Constant defining size of the array returned by
-   * {@link PathIterator#currentSegment(double[])} method. More nformation can be
+   * {@link PathIterator#currentSegment(double[])} method. More information can be
    * found <a href=
    * "http://docs.oracle.com/javase/7/docs/api/java/awt/geom/PathIterator.html#currentSegment(double[])"
    * >here</a>
@@ -473,8 +479,9 @@ public class ProjectRestImpl extends BaseRestImpl {
     }
   }
 
-  public ProjectMetaData addProject(String token, String projectId, MultiValueMap<String, String> data, String path)
+  public ProjectMetaData addProject(String token, String projectId, MultiValueMap<String, Object> data, String path)
       throws SecurityException, QueryException, IOException {
+    logger.info(data);
     AuthenticationToken authenticationToken = getUserService().getToken(token);
     User user = getUserService().getUserByToken(authenticationToken);
     Project project = getProjectService().getProjectByProjectId(projectId, authenticationToken);
@@ -493,6 +500,12 @@ public class ProjectRestImpl extends BaseRestImpl {
     }
     IConverter parser = getModelParser(parserClass);
 
+    List<ZipEntryFile> zipEntries = extractZipEntries(data);
+    params.complex(zipEntries.size() > 0);
+    for (ZipEntryFile entry : zipEntries) {
+      params.addZipEntry(entry);
+    }
+
     params.addUser(user.getLogin(), null);
     params.async(true);
     params.parser(parser);
@@ -504,7 +517,7 @@ public class ProjectRestImpl extends BaseRestImpl {
     params.notifyEmail(getFirstValue(data.get("notify-email")));
     params.projectDir(directory);
     params.projectDisease(getFirstValue(data.get("disease")));
-    params.projectFile(new ByteArrayInputStream(fileContent.getBytes(StandardCharsets.UTF_8)));
+    params.projectFile(new ByteArrayInputStream(fileContent.getBytes()));
     params.projectId(projectId);
     params.projectName(getFirstValue(data.get("name")));
     params.projectOrganism(getFirstValue(data.get("organism")));
@@ -525,12 +538,57 @@ public class ProjectRestImpl extends BaseRestImpl {
     return getProject(projectId, token);
   }
 
-  private String getFirstValue(List<String> list) {
+  protected List<ZipEntryFile> extractZipEntries(MultiValueMap<String, Object> data) {
+    int fileIndex = 0;
+    List<ZipEntryFile> result = new ArrayList<>();
+    while (data.get("zip-entries[" + fileIndex + "][_filename]") != null) {
+      ZipEntryFile entry = null;
+      String entryType = (String) data.get("zip-entries[" + fileIndex + "][_type]").get(0);
+      String filename = (String) data.get("zip-entries[" + fileIndex + "][_filename]").get(0);
+      if ("MAP".equalsIgnoreCase(entryType)) {
+        String submodelTypeKey = "zip-entries[" + fileIndex + "][_data][type][id]";
+        String rootKey = "zip-entries[" + fileIndex + "][_data][root]";
+        String mappingKey = "zip-entries[" + fileIndex + "][_data][mapping]";
+        SubmodelType mapType = SubmodelType.valueOf((String) data.get(submodelTypeKey).get(0));
+        String name = (String) data.get("zip-entries[" + fileIndex + "][_data][name]").get(0);
+        Boolean root = getBoolValue(data.get(rootKey), false);
+        Boolean mapping = getBoolValue(data.get(mappingKey), false);
+
+        entry = new ModelZipEntryFile(filename, name, root, mapping, mapType);
+      } else if ("OVERLAY".equalsIgnoreCase(entryType)) {
+        String name = (String) data.get("zip-entries[" + fileIndex + "][_data][name]").get(0);
+        String description = (String) data.get("zip-entries[" + fileIndex + "][_data][description]").get(0);
+        entry = new LayoutZipEntryFile(filename, name, description);
+      } else if ("IMAGE".equalsIgnoreCase(entryType)) {
+        entry = new ImageZipEntryFile(filename);
+      } else {
+        throw new Error("Unknown entry type: " + entryType);
+      }
+      fileIndex++;
+      result.add(entry);
+
+    }
+    return result;
+  }
+
+  private Boolean getBoolValue(List<Object> list, boolean defaultValue) {
+    if (list == null) {
+      return defaultValue;
+    }
+    Object obj = list.get(0);
+    if (obj instanceof Boolean) {
+      return (Boolean) list.get(0);
+    } else {
+      return "true".equalsIgnoreCase((String) obj);
+    }
+  }
+
+  private String getFirstValue(List<Object> list) {
     if (list == null) {
       return null;
     }
     if (list.size() > 0) {
-      return list.get(0);
+      return (String) list.get(0);
     }
     return null;
   }
diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java
index 97482c221658f95a21ec2112e04bc8b5b9f06757..f7bfb87b87c29fba1c290f12b0373a8bcb152a45 100644
--- a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java
+++ b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java
@@ -2,6 +2,7 @@ package lcsb.mapviewer.api.projects;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
@@ -9,6 +10,7 @@ import static org.mockito.Mockito.times;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -19,6 +21,8 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
 
 import com.google.gson.Gson;
 
@@ -28,6 +32,10 @@ import lcsb.mapviewer.api.RestTestFunctions;
 import lcsb.mapviewer.common.exception.InvalidArgumentException;
 import lcsb.mapviewer.converter.graphics.PdfImageGenerator;
 import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser;
+import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
+import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
+import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
+import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.cache.FileEntry;
 import lcsb.mapviewer.model.map.MiriamType;
@@ -36,222 +44,260 @@ import lcsb.mapviewer.services.interfaces.IModelService;
 import lcsb.mapviewer.services.interfaces.IProjectService;
 
 public class ProjectRestImplTest extends RestTestFunctions {
-	Logger					logger = Logger.getLogger(ProjectRestImplTest.class);
-
-	@Autowired
-	ProjectRestImpl	_projectRestImpl;
-
-	@Autowired
-	IModelService		modelService;
-
-	@AfterClass
-	public static void tearDownAfterClass() throws Exception {
-	}
-
-	@Before
-	public void setUp() throws Exception {
-	}
-
-	@After
-	public void tearDown() throws Exception {
-	}
-
-	@Test
-	public void testGetModelAsImageForInvalidConverter() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			projectRest.getModelAsImage(token.getId(), "sample", "0", "", "", "", "", "");
-			fail("Exception expected");
-		} catch (InvalidArgumentException e) {
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetModelAsImage() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			FileEntry result = projectRest.getModelAsImage(token.getId(), "sample", "0", PdfImageGenerator.class.getCanonicalName(), "", "", "", "");
-			assertNotNull(result);
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetModelDataDependencies() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			ProjectMetaData result = projectRest.getProject("sample", token.getId());
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(result));
-			Mockito.verify(projectRest.getModelService(), times(0)).getLastModelByProjectId(anyString(), any());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test(expected = ObjectNotFoundException.class)
-	public void testGetInvalidMetaData() throws Exception {
-		ProjectRestImpl projectRest = createMockProjectRest(null);
-		projectRest.getProject("unknown_model_id", token.getId());
-	}
-
-	@Test
-	public void testGetModelAsImage2() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			projectRest.getModelAsImage(token.getId(), "sample", "0", PdfImageGenerator.class.getCanonicalName(), "", "", "", "");
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetModelAsFileModel() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			projectRest.getModelAsModelFile(token.getId(), "sample", "0", "", "", "", "", "");
-			fail("Exception expected");
-		} catch (QueryException e) {
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetModelAsFileModel2() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			projectRest.getModelAsModelFile(token.getId(), "sample", "0", CellDesignerXmlParser.class.getCanonicalName(), "", "", "", "0,0;90,0;90,90;90,0");
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetModelAsFileModel3() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			projectRest.getModelAsModelFile(token.getId(), "sample", "0", "", "", "", "", "0,0;90,0;90,90;90,0");
-			fail("Exception expected");
-		} catch (QueryException e) {
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetProject() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			ProjectMetaData result = projectRest.getProject("sample", token.getId());
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(result));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testUpdateProject() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			Map<String,String> disease = new HashMap<>();
-			disease.put("type",MiriamType.MESH_2012.name());
-			disease.put("resource", "D010300");
-			Map<String, Object> data = new HashMap<>();
-			data.put("version", "1");
-			data.put("name", "test");
-			data.put("organism", null);
-			data.put("disease", disease);
-			data.put("projectId", "sample");
-			data.put("id", "0");
-			projectRest.updateProject(adminToken.getId(), "sample", data);
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetProjects() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			List<ProjectMetaData> result = projectRest.getProjects(token.getId());
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(result));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetStatistics() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
-			Map<String, Object> result = projectRest.getStatistics("sample", token.getId());
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(result));
-
-			Map<?, ?> elementAnnotations = (Map<?, ?>) result.get("elementAnnotations");
-			assertEquals(elementAnnotations.get(MiriamType.CAS), 0);
-			assertEquals(elementAnnotations.get(MiriamType.ENTREZ), 1);
-
-			Map<?, ?> reactionAnnotations = (Map<?, ?>) result.get("reactionAnnotations");
-			assertEquals(reactionAnnotations.get(MiriamType.ENTREZ), 0);
-			assertEquals(reactionAnnotations.get(MiriamType.PUBMED), 1);
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testGetMetaDataForComplexWithImages() throws Exception {
-		try {
-			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/complex_model_with_submaps.zip");
-			ProjectMetaData result = projectRest.getProject("sample", token.getId());
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(result));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	private ProjectRestImpl createMockProjectRest(String string) throws Exception {
-		Model model = null;
-		Project project = null;
-		if (string != null) {
-			project = new Project();
-			model = super.getModelForFile(string, true);
-			project.addModel(model);
-			project.setProjectId(model.getName());
-		}
-		IModelService mockModelService = Mockito.mock(IModelService.class);
-		Mockito.when(mockModelService.getLastModelByProjectId(anyString(), any())).thenReturn(model);
-		_projectRestImpl.setModelService(mockModelService);
-
-		IProjectService projectServiceMock = Mockito.mock(IProjectService.class);
-		Mockito.when(projectServiceMock.getProjectByProjectId(anyString(), any())).thenReturn(project);
-		List<Project> projects = new ArrayList<>();
-		projects.add(project);
-		Mockito.when(projectServiceMock.getAllProjects(any())).thenReturn(projects);
-		_projectRestImpl.setProjectService(projectServiceMock);
-
-		return _projectRestImpl;
-	}
+  Logger logger = Logger.getLogger(ProjectRestImplTest.class);
+
+  @Autowired
+  ProjectRestImpl _projectRestImpl;
+
+  @Autowired
+  IModelService modelService;
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+  }
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void testGetModelAsImageForInvalidConverter() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      projectRest.getModelAsImage(token.getId(), "sample", "0", "", "", "", "", "");
+      fail("Exception expected");
+    } catch (InvalidArgumentException e) {
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetModelAsImage() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      FileEntry result = projectRest.getModelAsImage(token.getId(), "sample", "0",
+          PdfImageGenerator.class.getCanonicalName(), "", "", "", "");
+      assertNotNull(result);
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetModelDataDependencies() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      ProjectMetaData result = projectRest.getProject("sample", token.getId());
+      Gson gson = new Gson();
+      assertNotNull(gson.toJson(result));
+      Mockito.verify(projectRest.getModelService(), times(0)).getLastModelByProjectId(anyString(), any());
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test(expected = ObjectNotFoundException.class)
+  public void testGetInvalidMetaData() throws Exception {
+    ProjectRestImpl projectRest = createMockProjectRest(null);
+    projectRest.getProject("unknown_model_id", token.getId());
+  }
+
+  @Test
+  public void testGetModelAsImage2() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      projectRest.getModelAsImage(token.getId(), "sample", "0", PdfImageGenerator.class.getCanonicalName(), "", "", "",
+          "");
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetModelAsFileModel() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      projectRest.getModelAsModelFile(token.getId(), "sample", "0", "", "", "", "", "");
+      fail("Exception expected");
+    } catch (QueryException e) {
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetModelAsFileModel2() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      projectRest.getModelAsModelFile(token.getId(), "sample", "0", CellDesignerXmlParser.class.getCanonicalName(), "",
+          "", "", "0,0;90,0;90,90;90,0");
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetModelAsFileModel3() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      projectRest.getModelAsModelFile(token.getId(), "sample", "0", "", "", "", "", "0,0;90,0;90,90;90,0");
+      fail("Exception expected");
+    } catch (QueryException e) {
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetProject() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      ProjectMetaData result = projectRest.getProject("sample", token.getId());
+      Gson gson = new Gson();
+      assertNotNull(gson.toJson(result));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testUpdateProject() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      Map<String, String> disease = new HashMap<>();
+      disease.put("type", MiriamType.MESH_2012.name());
+      disease.put("resource", "D010300");
+      Map<String, Object> data = new HashMap<>();
+      data.put("version", "1");
+      data.put("name", "test");
+      data.put("organism", null);
+      data.put("disease", disease);
+      data.put("projectId", "sample");
+      data.put("id", "0");
+      projectRest.updateProject(adminToken.getId(), "sample", data);
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetProjects() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      List<ProjectMetaData> result = projectRest.getProjects(token.getId());
+      Gson gson = new Gson();
+      assertNotNull(gson.toJson(result));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetStatistics() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+      Map<String, Object> result = projectRest.getStatistics("sample", token.getId());
+      Gson gson = new Gson();
+      assertNotNull(gson.toJson(result));
+
+      Map<?, ?> elementAnnotations = (Map<?, ?>) result.get("elementAnnotations");
+      assertEquals(elementAnnotations.get(MiriamType.CAS), 0);
+      assertEquals(elementAnnotations.get(MiriamType.ENTREZ), 1);
+
+      Map<?, ?> reactionAnnotations = (Map<?, ?>) result.get("reactionAnnotations");
+      assertEquals(reactionAnnotations.get(MiriamType.ENTREZ), 0);
+      assertEquals(reactionAnnotations.get(MiriamType.PUBMED), 1);
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testGetMetaDataForComplexWithImages() throws Exception {
+    try {
+      ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/complex_model_with_submaps.zip");
+      ProjectMetaData result = projectRest.getProject("sample", token.getId());
+      Gson gson = new Gson();
+      assertNotNull(gson.toJson(result));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testExtractZipEntries() throws Exception {
+    try {
+      MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
+      data.put("zip-entries[0][_type]",createLinkedList("MAP")); 
+      data.put("zip-entries[0][_filename]",createLinkedList("main.xml")); 
+      data.put("zip-entries[0][_data][root]",createLinkedList("true")); 
+      data.put("zip-entries[0][_data][name]",createLinkedList("main")); 
+      data.put("zip-entries[0][_data][type][id]",createLinkedList("UNKNOWN")); 
+      data.put("zip-entries[0][_data][type][name]",createLinkedList("Unknown")); 
+      data.put("zip-entries[1][_type]",createLinkedList("OVERLAY")); 
+      data.put("zip-entries[1][_filename]",createLinkedList("layouts/goodschema.txt")); 
+      data.put("zip-entries[1][_data][name]",createLinkedList("example name")); 
+      data.put("zip-entries[1][_data][description]",createLinkedList("layout description")); 
+      data.put("zip-entries[2][_type]",createLinkedList("IMAGE")); 
+      data.put("zip-entries[2][_filename]",createLinkedList("images/test.png")); 
+      List<ZipEntryFile> result = _projectRestImpl.extractZipEntries(data);
+      assertNotNull(result);
+      assertEquals(3, result.size());
+      assertTrue(result.get(0) instanceof ModelZipEntryFile);
+      assertTrue(result.get(1) instanceof LayoutZipEntryFile);
+      assertTrue(result.get(2) instanceof ImageZipEntryFile);
+      
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  private LinkedList<Object> createLinkedList(Object string) {
+    LinkedList<Object> result = new LinkedList<>();
+    result.add(string);
+    return result;
+  }
+
+  private ProjectRestImpl createMockProjectRest(String string) throws Exception {
+    Model model = null;
+    Project project = null;
+    if (string != null) {
+      project = new Project();
+      model = super.getModelForFile(string, true);
+      project.addModel(model);
+      project.setProjectId(model.getName());
+    }
+    IModelService mockModelService = Mockito.mock(IModelService.class);
+    Mockito.when(mockModelService.getLastModelByProjectId(anyString(), any())).thenReturn(model);
+    _projectRestImpl.setModelService(mockModelService);
+
+    IProjectService projectServiceMock = Mockito.mock(IProjectService.class);
+    Mockito.when(projectServiceMock.getProjectByProjectId(anyString(), any())).thenReturn(project);
+    List<Project> projects = new ArrayList<>();
+    projects.add(project);
+    Mockito.when(projectServiceMock.getAllProjects(any())).thenReturn(projects);
+    _projectRestImpl.setProjectService(projectServiceMock);
+
+    return _projectRestImpl;
+  }
 
 }