diff --git a/CHANGELOG b/CHANGELOG index 180fa045336cffa4d3adfab04e0c6de15a08dd2f..0bb8da966a4b3917e0e276515a42b8578ad4fa57 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,59 @@ minerva (12.3.0~alpha.0) unstable; urgency=low - * Feature: annotators are more flexible - you can define set of input and outputs used by + * Feature: annotators are more flexible - you can define set of input and outputs used by annotator (#617) * Small improvement: added current username next to logout button in admin panel (#660) * Small improvement: New comment dialog does not contain content of previous comment dialog (#680) +minerva (12.2.0~beta.2) unstable; urgency=medium + * Bug fix: order of the overlays is defined explicitly also for general + overlays (#684) + * Bug fix: files are saved in proper folder (#694, #670) + * Bug fix: removing comments in a project asked for confirmation twice after + edit project dialog for specific project was opened; closed and opened + again (#697) + * Bug fix: clicking on compartment border inside pathway should return + compartment, not a pathway (#324) + * Bug fix: clicking outside of the element sometimes resulted with the + invalid element highlighted (#324) + * Bug fix: parent compartment/pathway use proper type name in left panel + (#324) + * Bug fix: editing/removing project requires Map Management privilege (#681) + * Bug fix: when removeAllListeners is called list of registered listeners is + cleaned (#687) + * Bug fix: when plugin is removed the html elements associated with it are + removed as well (#686) + * Bug fix: when creating new user default privileges are set properly (#692) + * Bug fix: import from SBML issue - reaction with product and modifier being + the same element caused exception (#703) + * Bug fix: plugin can be added after plugin has been removed in admin plugin + panel (#686) + * Bug fix: too many annotations (>=100) caused misaligning in the left panel + (#708) + * Bug fix: text was outside the popup window (#711) + * Bug fix: custom semantic zooming contains multiple overlays checkbox was + disabled (#715) + * Bug fix: export to svg contains viewBox info (#716) + * Bug fix: import from sbml with layout could crash when two elements + occupied the exact same position (#717) + * Bug fix: overlays added via API couldn't be visualized after refresh (#718) + * Bug fix: Safari sometimes cached server responses and used wrong data, for + example in admin panel configuration tab (#719) + * Bug fix: description of transcription site is centered (#720) + * Bug fix: selecting too few parameters in export doesn't throw reportable + error (#721) + * Bug fix: changes in selected checkbox in add project dialog block UI (#722) + * Bug fix: providing invalid overlay id in url could break minerva (#726) + * Bug fix: removing project that doesn't exist doesn't cause 500 error (#723) + * Bug fix: Editing project with images and submaps could cause a problem + (#725) + * Bug fix: opening map with invalid id shows proper error message (#724) + * Bug fix: Fixing an issue where search panel was not properly resized on + expansion (#682) + * Small improvement: highlighting table rows in admin panel uses better + contrast color (#706) + + -- Piotr Gawron <piotr.gawron@uni.lu> Wed, 20 Feb 2019 14:00:00 +0200 + minerva (12.2.0~beta.1) unstable; urgency=medium * Small improvement: list of publication can be filtered by submap (#614) * Small improvement: report bug utility shows confirmation dialog on success diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java index d5d6ceeda4ef964d18e8fd32b51dcad732e232ef..5bfc3deb6f3345a5df47b492bdc92ccf0e3eb984 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java @@ -633,32 +633,6 @@ public abstract class ElementAnnotator extends CachableInterface { return ""; } - /** - * Provides description for {@link ElementAnnotator} and {@link MiriamType} pair - * to be used in the frontend. - * - * @param mt - * {@link MiriamType} annotated by this annotator. - * @return the description - */ - public String getDescription(MiriamType mt) { - return ""; - } - - /** - * Provides description for {@link ElementAnnotator}, {@link MiriamType} and - * {@link MiriamRelationType} triplet to be used in the frontend. - * - * @param mt - * {@link MiriamType} annotated by this annotator - * @param relationType - * {@link MiriamRelationType} annotated by this annotator. - * @return the description - */ - public String getDescription(MiriamType mt, MiriamRelationType relationType) { - return ""; - } - /** * Returns list with definitions of the parameters available for this annotator. * diff --git a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/SvgImageGenerator.java b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/SvgImageGenerator.java index 6a1aa4f2037cc2e0993a67682f495f6e7ac32326..41cc790acb4dfaaf9270fe540a6dba7fe388a690 100644 --- a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/SvgImageGenerator.java +++ b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/SvgImageGenerator.java @@ -1,17 +1,20 @@ package lcsb.mapviewer.converter.graphics; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; -import lcsb.mapviewer.common.MimeType; -import lcsb.mapviewer.common.exception.NotImplementedException; - import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.svggen.SVGGraphics2DIOException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import lcsb.mapviewer.common.MimeType; +import lcsb.mapviewer.common.exception.NotImplementedException; /** * This class implements SVG generator for the image object.This class uses @@ -21,6 +24,11 @@ import org.w3c.dom.Document; * */ public class SvgImageGenerator extends AbstractImageGenerator { + + /** + * Root dom element. We need it to be able to provide viewBox. + */ + private Element root; @Override protected void createImageObject(final double width, final double height) { @@ -32,14 +40,17 @@ public class SvgImageGenerator extends AbstractImageGenerator { String svgNS = "http://www.w3.org/2000/svg"; Document document = domImpl.createDocument(svgNS, "svg", null); - // Create an instance of the SVG Generator. - setGraphics(new SVGGraphics2D(document)); + // Create an instance of the SVG Generator. + SVGGraphics2D graphics =new SVGGraphics2D(document); + root = graphics.getRoot(); + root.setAttributeNS(null, "viewBox", "0 0 "+(int)width+" "+(int)height); + setGraphics(graphics); } @Override - public void saveToFileImplementation(final String fileName) throws SVGGraphics2DIOException { - ((SVGGraphics2D) getGraphics()).stream(fileName); + public void saveToFileImplementation(final String fileName) throws IOException { + saveToOutputStreamImplementation(new FileOutputStream(fileName)); } /** @@ -63,8 +74,9 @@ public class SvgImageGenerator extends AbstractImageGenerator { @Override public void saveToOutputStreamImplementation(OutputStream os) throws IOException { - ((SVGGraphics2D) getGraphics()).stream(new OutputStreamWriter(os)); - + //for some reason if you remove this line either viewbox is not define or content is not there + ((SVGGraphics2D) getGraphics()).getRoot(root); + ((SVGGraphics2D) getGraphics()).stream(root, new OutputStreamWriter(os)); } @Override diff --git a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/bioEntity/element/species/SpeciesConverter.java b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/bioEntity/element/species/SpeciesConverter.java index 158269f20afe6240174ce7a288180331f9906d68..980ac89b8315020b225cf2080019aa219c482a3b 100644 --- a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/bioEntity/element/species/SpeciesConverter.java +++ b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/bioEntity/element/species/SpeciesConverter.java @@ -699,7 +699,9 @@ public abstract class SpeciesConverter<T extends Species> extends ElementConvert y = transcriptionSite.getPosition().getY() - DEFAULT_MODIFICATION_DIAMETER - DEFAULT_SPECIES_FONT_SIZE / 2; if (transcriptionSite.getName() != null && !transcriptionSite.getName().isEmpty()) { - drawText(new Point2D.Double(x, y), transcriptionSite.getName(), graphics, true, false); + Point2D centerTextPoint = new Point2D.Double( + transcriptionSite.getPosition().getX() + transcriptionSite.getWidth() / 2, y); + drawText(centerTextPoint, transcriptionSite.getName(), graphics, true, false); } } diff --git a/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AllGraphicsTests.java b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AllGraphicsTests.java index b0a0d88529eda0deb86d53648ff20b75b1d97a53..3d6271639f56196f983535047b0ef02315498a18 100644 --- a/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AllGraphicsTests.java +++ b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AllGraphicsTests.java @@ -19,6 +19,7 @@ import lcsb.mapviewer.converter.graphics.placefinder.AllPlaceFinderTest; MapGeneratorTest.class, NormalImageGeneratorTest.class, PdfImageGeneratorTest.class, + SvgImageGeneratorTest.class }) public class AllGraphicsTests { diff --git a/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/SvgImageGeneratorTest.java b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/SvgImageGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ffd2eec03dba74dee023562c8cff26b94e0d0800 --- /dev/null +++ b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/SvgImageGeneratorTest.java @@ -0,0 +1,58 @@ +package lcsb.mapviewer.converter.graphics; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.model.ModelFullIndexed; +import lcsb.mapviewer.model.map.species.Complex; + +public class SvgImageGeneratorTest extends GraphicsTestFunctions { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testSaveToFile() throws Exception { + try { + Model model = createCompartmentModel(); + + SvgImageGenerator sig = new SvgImageGenerator(new AbstractImageGenerator.Params().model(model)); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + sig.saveToOutputStream(output); + + assertTrue(output.toString().contains("viewBox")); + + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + private Model createCompartmentModel() { + Model model = new ModelFullIndexed(null); + model.setWidth(526); + model.setHeight(346); + Complex alias = new Complex("1"); + alias.setName("a"); + alias.setX(300); + alias.setY(90); + alias.setWidth(100); + alias.setHeight(50); + model.addElement(alias); + + return model; + } + +} diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java index 27f45b5e512edf20fd5f678a701b2c47d631c3e3..2f47b556f3e0ecbb3691a40a1e821f20042ed767 100644 --- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java @@ -156,7 +156,7 @@ public class SbmlParser extends Converter { if (element instanceof Complex && element != species) { Complex complex = (Complex) element; if (complex.getWidth() != 0 || complex.getHeight() != 0) { - if (complex.contains(species)) { + if (complex.contains(species) && complex.getSize() > species.getSize()) { if (result == null) { result = complex; } else if (result.getSize() > complex.getSize()) { diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/reaction/SbmlReactionParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/reaction/SbmlReactionParser.java index bf6aac35adaa11a56cd4726e487d216883d42858..9c865296714fb03cdd9e4886dd24646da93ff96b 100644 --- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/reaction/SbmlReactionParser.java +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/reaction/SbmlReactionParser.java @@ -133,6 +133,9 @@ public class SbmlReactionParser extends SbmlBioEntityParser { nodeClass = node.getClass(); } else if (node.getClass().isAssignableFrom(nodeClass)) { minervaNode = node; + } else if (nodeClass.isAssignableFrom(node.getClass())) { + minervaNode = node; + nodeClass = node.getClass(); } } } @@ -391,9 +394,10 @@ public class SbmlReactionParser extends SbmlBioEntityParser { case SUBSTRATE: nodeClass = Reactant.class; break; - case UNDEFINED: case MODIFIER: - nodeClass = null; + nodeClass = Modifier.class; + break; + case UNDEFINED: break; } } diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java index 1e2ecd69f2fe52546509453e9edc7ed2e6dbebf2..519b78264a8169f32302bb865802822dc58f8cc4 100644 --- a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java +++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java @@ -6,7 +6,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.FileNotFoundException; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; @@ -14,6 +16,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import lcsb.mapviewer.commands.CreateHierarchyCommand; import lcsb.mapviewer.common.EventStorageLoggerAppender; import lcsb.mapviewer.converter.ConverterParams; import lcsb.mapviewer.converter.InvalidInputDataExecption; @@ -21,6 +24,7 @@ import lcsb.mapviewer.model.map.compartment.Compartment; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.reaction.Reaction; import lcsb.mapviewer.model.map.reaction.ReactionNode; +import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.map.species.Gene; import lcsb.mapviewer.model.map.species.GenericProtein; import lcsb.mapviewer.model.map.species.Species; @@ -203,4 +207,37 @@ public class SbmlParserTest { assertEquals(1, getWarnings().size()); } + @Test + public void testCyclicComplexes() throws Exception { + try { + Model model = parser.createModel(new ConverterParams().filename("testFiles/cyclic_complex.xml")); + + new CreateHierarchyCommand(model, 8, 80).execute(); + + for (Species species : model.getSpeciesList()) { + Set<Element> parents = new HashSet<>(); + while (species.getComplex() != null) { + assertFalse("Cyclic nesting", parents.contains(species.getComplex())); + species= species.getComplex(); + parents.add(species); + } + + } + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testCyclicComplsexes() throws Exception { + try { + Model model = parser.createModel(new ConverterParams().filename("testFiles/reaction_with_species_used_twice.xml")); + assertEquals(1, model.getReactions().size()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + } diff --git a/converter-sbml/testFiles/cyclic_complex.xml b/converter-sbml/testFiles/cyclic_complex.xml new file mode 100644 index 0000000000000000000000000000000000000000..1bcf87dc149f0e88b0d1e8a89b6ff16dd8401796 --- /dev/null +++ b/converter-sbml/testFiles/cyclic_complex.xml @@ -0,0 +1,958 @@ +<?xml version="1.0" encoding="UTF-8"?> +<sbml xmlns="http://www.sbml.org/sbml/level3/version1/core" xmlns:layout="http://www.sbml.org/sbml/level3/version1/layout/version1" level="3" version="1" layout:required="false"> + <model substanceUnits="mole" timeUnits="second" extentUnits="mole"> + <listOfUnitDefinitions> + <unitDefinition id="per_second"> + <listOfUnits> + <unit kind="second" exponent="-1" scale="0" multiplier="1"/> + </listOfUnits> + </unitDefinition> + </listOfUnitDefinitions> + <listOfCompartments> + <compartment id="comp_0" name="compartment1" constant="false"/> + </listOfCompartments> + <listOfSpecies> + <species sboTerm="SBO:0000297" id="species_7" name="BasalACh2" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_10" name="BasalACh" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_8" name="ActiveACh2" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_5" name="ActiveACh" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000247" id="species_2" name="Basal" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_0" name="IntermediateACh" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_1" name="IntermediateACh2" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_6" name="DesensitisedACh" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000247" id="species_4" name="Desensitised" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000297" id="species_3" name="DesensitisedACh2" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000247" id="species_11" name="Intermediate" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000247" id="species_9" name="Active" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + </listOfSpecies> + <listOfReactions> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_14" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_10" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_7" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_0" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_7" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_8" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_1" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_10" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_5" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_11" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_8" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_1" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_3" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_5" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_8" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_10" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_5" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_0" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_5" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_2" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_10" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_2" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_0" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_1" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_6" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_9" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_5" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_15" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_1" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_3" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_13" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_2" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_9" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_16" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_0" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_6" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_9" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_4" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_6" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_4" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_6" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_3" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_8" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_9" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_11" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_7" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_11" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_0" constant="false"/> + </listOfProducts> + </reaction> + <reaction sboTerm="SBO:0000176" id="reaction_glyph_12" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_11" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_4" constant="false"/> + </listOfProducts> + </reaction> + </listOfReactions> + <layout:listOfLayouts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:layout="http://www.sbml.org/sbml/level3/version1/layout/version1"> + <layout:layout layout:id="predicted_layout"> + <layout:dimensions layout:width="634.5" layout:height="261.79151086467"/> + <layout:listOfCompartmentGlyphs> + <layout:compartmentGlyph layout:id="cg_comp_0" layout:compartment="comp_0"> + <layout:boundingBox layout:id="bb_cg_comp_0"> + <layout:position layout:x="0" layout:y="1"/> + <layout:dimensions layout:width="715" layout:height="302"/> + </layout:boundingBox> + </layout:compartmentGlyph> + </layout:listOfCompartmentGlyphs> + <layout:listOfSpeciesGlyphs> + <layout:speciesGlyph layout:id="sg_BLL__entityVertex_10008821_1129446" layout:species="species_7"> + <layout:boundingBox layout:id="bb_sg_BLL__entityVertex_10008821_1129446"> + <layout:position layout:x="348" layout:y="89"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_BL__entityVertex_10008824_1129452" layout:species="species_10"> + <layout:boundingBox layout:id="bb_sg_BL__entityVertex_10008824_1129452"> + <layout:position layout:x="515" layout:y="89"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_ALL__entityVertex_9999112_1129444" layout:species="species_8"> + <layout:boundingBox layout:id="bb_sg_ALL__entityVertex_9999112_1129444"> + <layout:position layout:x="120.5" layout:y="116"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_AL__entityVertex_10008823_1129455" layout:species="species_5"> + <layout:boundingBox layout:id="bb_sg_AL__entityVertex_10008823_1129455"> + <layout:position layout:x="489.5" layout:y="203"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_B__React0__React5" layout:species="species_2"> + <layout:boundingBox layout:id="bb_sg_B__React0__React5"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_IL__React10__React15" layout:species="species_0"> + <layout:boundingBox layout:id="bb_sg_IL__React10__React15"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_AL__React10__React3" layout:species="species_5"> + <layout:boundingBox layout:id="bb_sg_AL__React10__React3"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_ILL__React11__React16" layout:species="species_1"> + <layout:boundingBox layout:id="bb_sg_ILL__React11__React16"> + <layout:position layout:x="278.365520669189" layout:y="93.1137846275741"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_DL__React12__React13" layout:species="species_6"> + <layout:boundingBox layout:id="bb_sg_DL__React12__React13"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_D__React12__React14" layout:species="species_4"> + <layout:boundingBox layout:id="bb_sg_D__React12__React14"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_DLL__React13__React16" layout:species="species_3"> + <layout:boundingBox layout:id="bb_sg_DLL__React13__React16"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_I__React14__React7" layout:species="species_11"> + <layout:boundingBox layout:id="bb_sg_I__React14__React7"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_A__React3__React5" layout:species="species_9"> + <layout:boundingBox layout:id="bb_sg_A__React3__React5"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_IL__React7__React8" layout:species="species_0"> + <layout:boundingBox layout:id="bb_sg_IL__React7__React8"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_React0_BL" layout:species="species_10"> + <layout:boundingBox layout:id="bb_sg_React0_BL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_React15_DL" layout:species="species_6"> + <layout:boundingBox layout:id="bb_sg_React15_DL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_React8_ILL" layout:species="species_1"> + <layout:boundingBox layout:id="bb_sg_React8_ILL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_React9_A" layout:species="species_9"> + <layout:boundingBox layout:id="bb_sg_React9_A"> + <layout:position layout:x="305.019756063517" layout:y="181.453113676086"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_React9_I" layout:species="species_11"> + <layout:boundingBox layout:id="bb_sg_React9_I"> + <layout:position layout:x="305.019756063517" layout:y="69.8416647540904"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + </layout:listOfSpeciesGlyphs> + <layout:listOfReactionGlyphs> + <layout:reactionGlyph layout:id="rg_React1" layout:reaction="reaction_glyph_14"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="471.5" layout:y="109"/> + <layout:end layout:x="471.5" layout:y="109"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_BLL__entityVertex_10008821_1129446_product_1" layout:speciesGlyph="sg_BLL__entityVertex_10008821_1129446" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="471.5" layout:y="109"/> + <layout:end layout:x="388" layout:y="109"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_BL__entityVertex_10008824_1129452_substrate_1" layout:speciesGlyph="sg_BL__entityVertex_10008824_1129452" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="471.5" layout:y="109"/> + <layout:end layout:x="555" layout:y="109"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React2" layout:reaction="reaction_glyph_0"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="274.25" layout:y="122.5"/> + <layout:end layout:x="274.25" layout:y="122.5"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_BLL__entityVertex_10008821_1129446_substrate_1" layout:speciesGlyph="sg_BLL__entityVertex_10008821_1129446" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="274.25" layout:y="122.5"/> + <layout:end layout:x="388" layout:y="109"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_ALL__entityVertex_9999112_1129444_product_1" layout:speciesGlyph="sg_ALL__entityVertex_9999112_1129444" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="274.25" layout:y="122.5"/> + <layout:end layout:x="160.5" layout:y="136"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React6" layout:reaction="reaction_glyph_1"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="542.25" layout:y="166"/> + <layout:end layout:x="542.25" layout:y="166"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_BL__entityVertex_10008824_1129452_substrate_2" layout:speciesGlyph="sg_BL__entityVertex_10008824_1129452" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="542.25" layout:y="166"/> + <layout:end layout:x="555" layout:y="109"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_AL__entityVertex_10008823_1129455_product_1" layout:speciesGlyph="sg_AL__entityVertex_10008823_1129455" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="542.25" layout:y="166"/> + <layout:end layout:x="529.5" layout:y="223"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React11" layout:reaction="reaction_glyph_11"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="239.432760334595" layout:y="124.556892313787"/> + <layout:end layout:x="239.432760334595" layout:y="124.556892313787"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_ALL__entityVertex_9999112_1129444_substrate_1" layout:speciesGlyph="sg_ALL__entityVertex_9999112_1129444" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="239.432760334595" layout:y="124.556892313787"/> + <layout:end layout:x="160.5" layout:y="136"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_ILL__React11__React16_product_1" layout:speciesGlyph="sg_ILL__React11__React16" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="239.432760334595" layout:y="124.556892313787"/> + <layout:end layout:x="318.365520669189" layout:y="113.113784627574"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React4" layout:reaction="reaction_glyph_3"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345" layout:y="179.5"/> + <layout:end layout:x="345" layout:y="179.5"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_ALL__entityVertex_9999112_1129444_product_2" layout:speciesGlyph="sg_ALL__entityVertex_9999112_1129444" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345" layout:y="179.5"/> + <layout:end layout:x="160.5" layout:y="136"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_AL__entityVertex_10008823_1129455_substrate_1" layout:speciesGlyph="sg_AL__entityVertex_10008823_1129455" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345" layout:y="179.5"/> + <layout:end layout:x="529.5" layout:y="223"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React10" layout:reaction="reaction_glyph_10"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_IL__React10__React15_product_1" layout:speciesGlyph="sg_IL__React10__React15" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_AL__React10__React3_substrate_1" layout:speciesGlyph="sg_AL__React10__React3" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React0" layout:reaction="reaction_glyph_5"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="113.427939175209"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_B__React0__React5_substrate_1" layout:speciesGlyph="sg_B__React0__React5" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_React0_BL_product_1" layout:speciesGlyph="sg_React0_BL" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="81.2084891353298"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React8" layout:reaction="reaction_glyph_2"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="113.427939175209"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_IL__React7__React8_substrate_1" layout:speciesGlyph="sg_IL__React7__React8" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_React8_ILL_product_1" layout:speciesGlyph="sg_React8_ILL" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="81.2084891353298"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React3" layout:reaction="reaction_glyph_6"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_AL__React10__React3_product_1" layout:speciesGlyph="sg_AL__React10__React3" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_A__React3__React5_substrate_1" layout:speciesGlyph="sg_A__React3__React5" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React16" layout:reaction="reaction_glyph_15"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="347.802363386293" layout:y="129.380586921331"/> + <layout:end layout:x="347.802363386293" layout:y="129.380586921331"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_ILL__React11__React16_substrate_1" layout:speciesGlyph="sg_ILL__React11__React16" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="347.802363386293" layout:y="129.380586921331"/> + <layout:end layout:x="318.365520669189" layout:y="113.113784627574"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_DLL__React13__React16_product_1" layout:speciesGlyph="sg_DLL__React13__React16" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="347.802363386293" layout:y="129.380586921331"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React5" layout:reaction="reaction_glyph_13"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_B__React0__React5_substrate_2" layout:speciesGlyph="sg_B__React0__React5" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_A__React3__React5_product_1" layout:speciesGlyph="sg_A__React3__React5" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React15" layout:reaction="reaction_glyph_16"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="113.427939175209"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_IL__React10__React15_substrate_1" layout:speciesGlyph="sg_IL__React10__React15" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_React15_DL_product_1" layout:speciesGlyph="sg_React15_DL" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="113.427939175209"/> + <layout:end layout:x="377.239206103396" layout:y="81.2084891353298"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React12" layout:reaction="reaction_glyph_9"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_DL__React12__React13_product_1" layout:speciesGlyph="sg_DL__React12__React13" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_D__React12__React14_substrate_1" layout:speciesGlyph="sg_D__React12__React14" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React13" layout:reaction="reaction_glyph_4"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_DL__React12__React13_substrate_1" layout:speciesGlyph="sg_DL__React12__React13" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_DLL__React13__React16_product_2" layout:speciesGlyph="sg_DLL__React13__React16" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React9" layout:reaction="reaction_glyph_8"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345.019756063517" layout:y="145.647389215088"/> + <layout:end layout:x="345.019756063517" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_React9_A_substrate_1" layout:speciesGlyph="sg_React9_A" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345.019756063517" layout:y="145.647389215088"/> + <layout:end layout:x="345.019756063517" layout:y="201.453113676086"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_React9_I_product_1" layout:speciesGlyph="sg_React9_I" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="345.019756063517" layout:y="145.647389215088"/> + <layout:end layout:x="345.019756063517" layout:y="89.8416647540904"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React7" layout:reaction="reaction_glyph_7"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_I__React14__React7_substrate_1" layout:speciesGlyph="sg_I__React14__React7" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_IL__React7__React8_product_1" layout:speciesGlyph="sg_IL__React7__React8" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + <layout:reactionGlyph layout:id="rg_React14" layout:reaction="reaction_glyph_12"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_D__React12__React14_product_1" layout:speciesGlyph="sg_D__React12__React14" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_I__React14__React7_substrate_2" layout:speciesGlyph="sg_I__React14__React7" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="377.239206103396" layout:y="145.647389215088"/> + <layout:end layout:x="377.239206103396" layout:y="145.647389215088"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + </layout:listOfReactionGlyphs> + <layout:listOfTextGlyphs> + <layout:textGlyph layout:id="tg_BLL__entityVertex_10008821_1129446" layout:originOfText="sg_BLL__entityVertex_10008821_1129446" layout:graphicalObject="sg_BLL__entityVertex_10008821_1129446"> + <layout:boundingBox layout:id="bb_tg_BLL__entityVertex_10008821_1129446"> + <layout:position layout:x="348" layout:y="89"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_BL__entityVertex_10008824_1129452" layout:originOfText="sg_BL__entityVertex_10008824_1129452" layout:graphicalObject="sg_BL__entityVertex_10008824_1129452"> + <layout:boundingBox layout:id="bb_tg_BL__entityVertex_10008824_1129452"> + <layout:position layout:x="515" layout:y="89"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_ALL__entityVertex_9999112_1129444" layout:originOfText="sg_ALL__entityVertex_9999112_1129444" layout:graphicalObject="sg_ALL__entityVertex_9999112_1129444"> + <layout:boundingBox layout:id="bb_tg_ALL__entityVertex_9999112_1129444"> + <layout:position layout:x="120.5" layout:y="116"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_AL__entityVertex_10008823_1129455" layout:originOfText="sg_AL__entityVertex_10008823_1129455" layout:graphicalObject="sg_AL__entityVertex_10008823_1129455"> + <layout:boundingBox layout:id="bb_tg_AL__entityVertex_10008823_1129455"> + <layout:position layout:x="489.5" layout:y="203"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_B__React0__React5" layout:originOfText="sg_B__React0__React5" layout:graphicalObject="sg_B__React0__React5"> + <layout:boundingBox layout:id="bb_tg_B__React0__React5"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_IL__React10__React15" layout:originOfText="sg_IL__React10__React15" layout:graphicalObject="sg_IL__React10__React15"> + <layout:boundingBox layout:id="bb_tg_IL__React10__React15"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_AL__React10__React3" layout:originOfText="sg_AL__React10__React3" layout:graphicalObject="sg_AL__React10__React3"> + <layout:boundingBox layout:id="bb_tg_AL__React10__React3"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_ILL__React11__React16" layout:originOfText="sg_ILL__React11__React16" layout:graphicalObject="sg_ILL__React11__React16"> + <layout:boundingBox layout:id="bb_tg_ILL__React11__React16"> + <layout:position layout:x="278.365520669189" layout:y="93.1137846275741"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_DL__React12__React13" layout:originOfText="sg_DL__React12__React13" layout:graphicalObject="sg_DL__React12__React13"> + <layout:boundingBox layout:id="bb_tg_DL__React12__React13"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_D__React12__React14" layout:originOfText="sg_D__React12__React14" layout:graphicalObject="sg_D__React12__React14"> + <layout:boundingBox layout:id="bb_tg_D__React12__React14"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_DLL__React13__React16" layout:originOfText="sg_DLL__React13__React16" layout:graphicalObject="sg_DLL__React13__React16"> + <layout:boundingBox layout:id="bb_tg_DLL__React13__React16"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_I__React14__React7" layout:originOfText="sg_I__React14__React7" layout:graphicalObject="sg_I__React14__React7"> + <layout:boundingBox layout:id="bb_tg_I__React14__React7"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_A__React3__React5" layout:originOfText="sg_A__React3__React5" layout:graphicalObject="sg_A__React3__React5"> + <layout:boundingBox layout:id="bb_tg_A__React3__React5"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_IL__React7__React8" layout:originOfText="sg_IL__React7__React8" layout:graphicalObject="sg_IL__React7__React8"> + <layout:boundingBox layout:id="bb_tg_IL__React7__React8"> + <layout:position layout:x="337.239206103396" layout:y="125.647389215088"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_React0_BL" layout:originOfText="sg_React0_BL" layout:graphicalObject="sg_React0_BL"> + <layout:boundingBox layout:id="bb_tg_React0_BL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_React15_DL" layout:originOfText="sg_React15_DL" layout:graphicalObject="sg_React15_DL"> + <layout:boundingBox layout:id="bb_tg_React15_DL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_React8_ILL" layout:originOfText="sg_React8_ILL" layout:graphicalObject="sg_React8_ILL"> + <layout:boundingBox layout:id="bb_tg_React8_ILL"> + <layout:position layout:x="337.239206103396" layout:y="61.2084891353298"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_React9_A" layout:originOfText="sg_React9_A" layout:graphicalObject="sg_React9_A"> + <layout:boundingBox layout:id="bb_tg_React9_A"> + <layout:position layout:x="305.019756063517" layout:y="181.453113676086"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + <layout:textGlyph layout:id="tg_React9_I" layout:originOfText="sg_React9_I" layout:graphicalObject="sg_React9_I"> + <layout:boundingBox layout:id="bb_tg_React9_I"> + <layout:position layout:x="305.019756063517" layout:y="69.8416647540904"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:textGlyph> + </layout:listOfTextGlyphs> + </layout:layout> + </layout:listOfLayouts> + </model> +</sbml> diff --git a/converter-sbml/testFiles/reaction_with_species_used_twice.xml b/converter-sbml/testFiles/reaction_with_species_used_twice.xml new file mode 100644 index 0000000000000000000000000000000000000000..312d2da098471077d2b34c09b14c4f7df6606fdb --- /dev/null +++ b/converter-sbml/testFiles/reaction_with_species_used_twice.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<sbml xmlns="http://www.sbml.org/sbml/level3/version1/core" xmlns:layout="http://www.sbml.org/sbml/level3/version1/layout/version1" level="3" version="1" layout:required="false"> + <model substanceUnits="mole" timeUnits="second" extentUnits="mole"> + <listOfUnitDefinitions> + <unitDefinition id="per_second"> + <listOfUnits> + <unit kind="second" exponent="-1" scale="0" multiplier="1"/> + </listOfUnits> + </unitDefinition> + </listOfUnitDefinitions> + <listOfCompartments> + <compartment id="comp_0" name="chl" constant="false"/> + </listOfCompartments> + <listOfSpecies> + <species sboTerm="SBO:0000247" id="species_9" name="Lysine" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000327" id="species_13" name="Aspartate semialdehyde" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + <species sboTerm="SBO:0000252" id="species_1" name="DHDPS1" compartment="comp_0" initialAmount="0" hasOnlySubstanceUnits="true" boundaryCondition="false" constant="false"/> + </listOfSpecies> + <listOfReactions> + <reaction sboTerm="SBO:0000205" id="reaction_glyph_16" reversible="false" fast="false"> + <listOfReactants> + <speciesReference species="species_13" constant="false"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="species_9" constant="false"/> + </listOfProducts> + <listOfModifiers> + <modifierSpeciesReference species="species_9"/> + <modifierSpeciesReference species="species_1"/> + </listOfModifiers> + </reaction> + </listOfReactions> + <layout:listOfLayouts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:layout="http://www.sbml.org/sbml/level3/version1/layout/version1"> + <layout:layout layout:id="predicted_layout"> + <layout:dimensions layout:width="3003.53089771722" layout:height="1872.10846837268"/> + <layout:listOfCompartmentGlyphs> + <layout:compartmentGlyph layout:id="cg_comp_0" layout:compartment="comp_0"> + <layout:boundingBox layout:id="bb_cg_comp_0"> + <layout:position layout:x="0" layout:y="0"/> + <layout:dimensions layout:width="3084" layout:height="1912"/> + </layout:boundingBox> + </layout:compartmentGlyph> + </layout:listOfCompartmentGlyphs> + <layout:listOfSpeciesGlyphs> + <layout:speciesGlyph layout:id="sg_Lys__sa7661" layout:species="species_9"> + <layout:boundingBox layout:id="bb_sg_Lys__sa7661"> + <layout:position layout:x="993.6630278981" layout:y="253.764759320153"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_ASA__sa7670" layout:species="species_13"> + <layout:boundingBox layout:id="bb_sg_ASA__sa7670"> + <layout:position layout:x="579.395442443615" layout:y="576.651298580278"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + <layout:speciesGlyph layout:id="sg_Vdhdps1_DHDPS1" layout:species="species_1"> + <layout:boundingBox layout:id="bb_sg_Vdhdps1_DHDPS1"> + <layout:position layout:x="617.716819753301" layout:y="339.666214868463"/> + <layout:dimensions layout:width="80" layout:height="40"/> + </layout:boundingBox> + </layout:speciesGlyph> + </layout:listOfSpeciesGlyphs> + <layout:listOfReactionGlyphs> + <layout:reactionGlyph layout:id="rg_Vdhdps1" layout:reaction="reaction_glyph_16"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="826.529235170857" layout:y="435.208028950216"/> + <layout:end layout:x="826.529235170857" layout:y="435.208028950216"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + <layout:listOfSpeciesReferenceGlyphs> + <layout:speciesReferenceGlyph layout:id="srg_Lys__sa7661_modifier_2" layout:speciesGlyph="sg_Lys__sa7661" layout:role="modifier"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="1033.6630278981" layout:y="273.764759320153"/> + <layout:end layout:x="905.506328076439" layout:y="322.937359825161"/> + </layout:curveSegment> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="905.506328076439" layout:y="322.937359825161"/> + <layout:end layout:x="826.529235170857" layout:y="435.208028950216"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_Lys__sa7661_product_1" layout:speciesGlyph="sg_Lys__sa7661" layout:role="product"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="826.529235170857" layout:y="435.208028950216"/> + <layout:end layout:x="1033.6630278981" layout:y="273.764759320153"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_ASA__sa7670_substrate_1" layout:speciesGlyph="sg_ASA__sa7670" layout:role="substrate"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="826.529235170857" layout:y="435.208028950216"/> + <layout:end layout:x="619.395442443615" layout:y="596.651298580278"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + <layout:speciesReferenceGlyph layout:id="srg_Vdhdps1_DHDPS1_modifier_1" layout:speciesGlyph="sg_Vdhdps1_DHDPS1" layout:role="modifier"> + <layout:curve> + <layout:listOfCurveSegments> + <layout:curveSegment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="LineSegment"> + <layout:start layout:x="657.716819753301" layout:y="359.666214868463"/> + <layout:end layout:x="817.401465289127" layout:y="431.123446109071"/> + </layout:curveSegment> + </layout:listOfCurveSegments> + </layout:curve> + </layout:speciesReferenceGlyph> + </layout:listOfSpeciesReferenceGlyphs> + </layout:reactionGlyph> + </layout:listOfReactionGlyphs> + </layout:layout> + </layout:listOfLayouts> + </model> +</sbml> diff --git a/frontend-js/src/main/css/global.css b/frontend-js/src/main/css/global.css index 96d4b880bea3ea75bb3c2171c351f94f5e4a6f93..e6ae7e3baaefc2db36fba1de9f8afbc399e94c82 100644 --- a/frontend-js/src/main/css/global.css +++ b/frontend-js/src/main/css/global.css @@ -546,16 +546,28 @@ Plugin tabs overflow: auto; } -.minerva-annotation-row-odd { +.minerva-annotation-row-odd, .minerva-annotation-row-even { + height: 26px; padding: 5px; +} + +.minerva-annotation-row-odd { background-color: #EAEAEA; } .minerva-annotation-row-even { - padding: 5px; background-color: #ffffff; } +.minerva-annotation-counter { + min-width: 28px; + float: left; +} + +.minerva-annotation-body { + float: left; +} + .minerva-overview-button { color: #FFFFFF; height: 36px; @@ -954,7 +966,7 @@ a.adminLink:hover { right: .5em; } -.popover { +.minerva-map .popover { max-width: 800px; min-width: 390px; } @@ -1091,4 +1103,10 @@ input[type=file] { .minerva-select-annotator-dialog .multi-checkbox-list-selected-entry:hover { background: #f5f5f5; cursor: pointer; -} \ No newline at end of file +} +#minervaAppDiv table.dataTable.hover tbody tr:hover, +#minervaAppDiv table.dataTable.display tbody tr:hover, +#minervaAppDiv table.dataTable.display tbody tr:hover > .sorting_1, +#minervaAppDiv table.dataTable.order-column.stripe tbody tr:hover > .sorting_1 { + background-color: #D3D3D3; +} diff --git a/frontend-js/src/main/js/GuiConnector.js b/frontend-js/src/main/js/GuiConnector.js index 608ce9cf5681d04d2bd769e7de31cc612335aa9d..9b5189af4a1bba46bca7ace7be2b5af27a6a5027 100644 --- a/frontend-js/src/main/js/GuiConnector.js +++ b/frontend-js/src/main/js/GuiConnector.js @@ -7,7 +7,6 @@ var logger = require('./logger'); var Functions = require('./Functions'); var SecurityError = require('./SecurityError'); var ValidationError = require('./ValidationError'); -var GuiMessageError = require('./gui/GuiMessageError'); /** * This static global object contains set of functions that returns/set data in diff --git a/frontend-js/src/main/js/gui/GuiMessageError.js b/frontend-js/src/main/js/gui/GuiMessageError.js deleted file mode 100644 index 098c45d37b79cd047f9b5070d4770f9f799366e2..0000000000000000000000000000000000000000 --- a/frontend-js/src/main/js/gui/GuiMessageError.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; - -/* exported logger */ - -var logger = require('../logger'); - -/** - * - * @param {string} message - * @constructor - * @extends {Error} - */ -function GuiMessageError(message) { - this.message = message; - this.stack = (new Error(message)).stack; -} - -GuiMessageError.prototype = Object.create(Error.prototype); -GuiMessageError.prototype.constructor = GuiMessageError; - -module.exports = GuiMessageError; diff --git a/frontend-js/src/main/js/gui/Panel.js b/frontend-js/src/main/js/gui/Panel.js index 17c65b590bb6ee31015a4cebeab270a120b369e7..d0e8c98d23849c3cfde72903d084784f70077ac9 100644 --- a/frontend-js/src/main/js/gui/Panel.js +++ b/frontend-js/src/main/js/gui/Panel.js @@ -43,13 +43,15 @@ function Panel(params) { } self.setPanelName(params.panelName); + var element = $(self.getElement()); + element.addClass("minerva-panel"); if (params.scrollable) { - $(self.getElement()).addClass("pre-scrollable"); + element.addClass("pre-scrollable"); } else { - $(self.getElement()).css("overflow-y", "auto"); + element.css("overflow-y", "auto"); } - $(self.getElement()).css("position", "relative"); + element.css("position", "relative"); if (params.helpTip !== undefined) { self.setHelpTip(params.helpTip); } diff --git a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js index 9045e206b85812a4f7bca34bfdae1d205bfb12c0..411134234854650d415c029d0b196b145b2bee3d 100644 --- a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js +++ b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js @@ -293,15 +293,6 @@ AddProjectDialog.prototype.createGeneralTabContent = function () { help: "Check this checkbox to display each semantic zoom level in the General Overlays" })); - $("[name='project-semantic-zooming']", table).change(function () { - var disable = !$("[name='project-semantic-zooming']", table).is(":checked"); - $("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("disabled", disable); - if (disable) { - $("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("checked", false); - } - }); - $("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("disabled", !$("[name='project-semantic-zooming']", table).is(":checked")); - var saveProjectButton = Functions.createElement({ type: "button", name: "saveProject", @@ -797,10 +788,14 @@ AddProjectDialog.prototype.bindProjectUploadPreferences = function (user, type, var value = user.getPreferences().getProjectUpload()[type]; element.prop('checked', value); + GuiConnector.showProcessing(); element.change(function () { var data = new UserPreferences(); data.getProjectUpload()[type] = element.is(":checked"); - return ServerConnector.updateUserPreferences({user: user, preferences: data}).catch(GuiConnector.alert); + return ServerConnector.updateUserPreferences({ + user: user, + preferences: data + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); }); }; @@ -1266,7 +1261,9 @@ AddProjectDialog.prototype.setZipFileContent = function (file) { guiUtils.hideTab(self, $(".minerva-project-overview-images-tab", self.getElement())[0]); } - return self.setZipEntries(entries); + return self.setZipEntries(entries).then(function () { + self._filename = file.name; + }); }); } else { guiUtils.hideTab(self, $(".minerva-project-overlays-tab", self.getElement())[0]); diff --git a/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js b/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js index 46e98e86f244812b84a68470aea63f5d0bce1c7b..72f9007e1163463e7ace9ba9cdd194037e8171a8 100644 --- a/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js +++ b/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js @@ -105,6 +105,7 @@ ChooseAnnotatorsDialog.prototype.onChangeParameterValue = function (element, ann ChooseAnnotatorsDialog.prototype.saveAnnotatorsInfo = function (elementTypes, selectedAnnotators) { var self = this; selectedAnnotators = selectedAnnotators.slice(); + GuiConnector.showProcessing(); return self.getServerConnector().getLoggedUser().then(function (user) { var elementAnnotators = user.getPreferences()._elementAnnotators; @@ -115,7 +116,7 @@ ChooseAnnotatorsDialog.prototype.saveAnnotatorsInfo = function (elementTypes, se user: user, preferences: user.getPreferences() }); - }).catch(GuiConnector.alert); + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); }; /** diff --git a/frontend-js/src/main/js/gui/admin/ChooseValidatorsDialog.js b/frontend-js/src/main/js/gui/admin/ChooseValidatorsDialog.js index b279827a47eed8b8775c005aae8c5c68c73cc725..29c189bb8484b8c2e6e41ba80ec4348cb9d86725 100644 --- a/frontend-js/src/main/js/gui/admin/ChooseValidatorsDialog.js +++ b/frontend-js/src/main/js/gui/admin/ChooseValidatorsDialog.js @@ -160,6 +160,7 @@ ChooseValidatorsDialog.prototype.saveAnnotationsInfo = function (params) { var requiredAnnotators = params.requiredAnnotators; var validAnnotators = params.validAnnotators; var self = this; + GuiConnector.showProcessing(); return self.getServerConnector().getLoggedUser().then(function (user) { var data = new UserPreferences(); @@ -179,7 +180,7 @@ ChooseValidatorsDialog.prototype.saveAnnotationsInfo = function (params) { user: user, preferences: data }); - }).catch(GuiConnector.alert); + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); }; @@ -241,10 +242,11 @@ ChooseValidatorsDialog.prototype.createValidAnnotationsDualListBox = function (u var elementAnnotators = {}; elementAnnotators[elementType.className] = validAnnotations; data.setElementValidAnnotations(elementAnnotators); + GuiConnector.showProcessing(); return self.getServerConnector().updateUserPreferences({ user: user, preferences: data - }).catch(GuiConnector.alert); + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); }; checkboxList.addListener("select", function (element) { @@ -282,7 +284,11 @@ ChooseValidatorsDialog.prototype.createVerifyAnnotationsDualListBox = function ( "annotation-list": requiredAnnotationsData.list }; data.setElementRequiredAnnotations(elementRequiredAnnotations); - return self.getServerConnector().updateUserPreferences({user: user, preferences: data}).catch(GuiConnector.alert); + GuiConnector.showProcessing(); + return self.getServerConnector().updateUserPreferences({ + user: user, + preferences: data + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); } }); checkbox.checked = requiredAnnotationsData.requiredAtLeastOnce; @@ -343,7 +349,11 @@ ChooseValidatorsDialog.prototype.createVerifyAnnotationsDualListBox = function ( "annotation-list": requiredAnnotationsData.list }; data.setElementRequiredAnnotations(elementRequiredAnnotations); - return self.getServerConnector().updateUserPreferences({user: user, preferences: data}).catch(GuiConnector.alert); + GuiConnector.showProcessing(); + return self.getServerConnector().updateUserPreferences({ + user: user, + preferences: data + }).finally(GuiConnector.hideProcessing).catch(GuiConnector.alert); }; checkboxList.addListener("select", function (element) { diff --git a/frontend-js/src/main/js/gui/admin/CommentsAdminPanel.js b/frontend-js/src/main/js/gui/admin/CommentsAdminPanel.js index 432cd9ad99c5186f087f372da9e21c2b3f5c233b..cb021863d262a4846bd5f472ae09cf9dfd0b1f1b 100644 --- a/frontend-js/src/main/js/gui/admin/CommentsAdminPanel.js +++ b/frontend-js/src/main/js/gui/admin/CommentsAdminPanel.js @@ -66,6 +66,25 @@ CommentsAdminPanel.prototype._createGui = function () { }] }); + $("[name='commentsTable']", self.getElement()).on("click", "[name='removeComment']", function () { + var button = this; + return self.askConfirmRemoval({ + title: "Why do you want to remove this comment?", + input: true + }).then(function (param) { + if (param.status) { + return ServerConnector.removeComment({ + commentId: $(button).attr("data"), + reason: param.reason, + projectId: self.getProject().getProjectId() + }).then(function () { + $(button).after("<span>YES (" + param.reason + ")</span>"); + button.style.display = "none"; + }); + } + }).catch(GuiConnector.alert) + }); + }; /** @@ -93,24 +112,6 @@ CommentsAdminPanel.prototype.refreshComments = function () { data.push(self.commentToTableRow(comments[i])); } dataTable.clear().rows.add(data).draw(); - $("[name='commentsTable']", self.getElement()).on("click", "[name='removeComment']", function () { - var button = this; - return self.askConfirmRemoval({ - title: "Why do you want to remove this comment?", - input: true - }).then(function (param) { - if (param.status) { - return ServerConnector.removeComment({ - commentId: $(button).attr("data"), - reason: param.reason, - projectId: self.getProject().getProjectId() - }).then(function () { - $(button).after("<span>YES (" + param.reason + ")</span>"); - button.style.display = "none"; - }); - } - }).catch(GuiConnector.alert) - }); }); }; @@ -131,10 +132,10 @@ CommentsAdminPanel.prototype.commentToTableRow = function (comment) { }; var title = null; if (!comment.isRemoved()) { - var commentLink = "index.xhtml?id=" + projectId + - "&x=" + comment.getCoordinates().x + - "&y=" + comment.getCoordinates().y + - "&zoom=12" + + var commentLink = "index.xhtml?id=" + projectId + + "&x=" + comment.getCoordinates().x + + "&y=" + comment.getCoordinates().y + + "&zoom=12" + "&comments=on"; title = "<a href='" + commentLink + "' target='" + projectId + "'>" + comment.getTitle() + "</a>"; } else { @@ -158,12 +159,12 @@ CommentsAdminPanel.prototype.commentToTableRow = function (comment) { email = "N/A"; } - return [comment.getId(), - xss(title), - xss(author), - xss(email), - xss(comment.getContent()), - remove, + return [comment.getId(), + xss(title), + xss(author), + xss(email), + xss(comment.getContent()), + remove, toYesNo(comment.isPinned())]; }; diff --git a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js index d310210258c9dab50eb9ea9acd6202d6c43d6a89..0ab6f060e07666ae01f051e75ae626ce567c435e 100644 --- a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js +++ b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js @@ -237,7 +237,7 @@ MapsAdminPanel.prototype.projectToTableRow = function (project, row, user) { row[4] = status; var disabled = " disabled "; - if (user.hasPrivilege(self.getConfiguration().getPrivilegeType(PrivilegeType.ADD_MAP))) { + if (user.hasPrivilege(self.getConfiguration().getPrivilegeType(PrivilegeType.PROJECT_MANAGEMENT))) { disabled = ""; } row[5] = "<button name='showEditDialog' data='" + project.getProjectId() + "'" + disabled + "><i class='fa fa-edit' style='font-size:17px'></i></button>"; diff --git a/frontend-js/src/main/js/gui/export/ElementExportPanel.js b/frontend-js/src/main/js/gui/export/ElementExportPanel.js index 5cbc4053b1eed4ea59c88202dda7fcb3ade3fb85..b40080133c3afbcec6d093ab3297d46b0e118357 100644 --- a/frontend-js/src/main/js/gui/export/ElementExportPanel.js +++ b/frontend-js/src/main/js/gui/export/ElementExportPanel.js @@ -3,7 +3,7 @@ /* exported logger */ var AbstractExportPanel = require('./AbstractExportPanel'); -var GuiMessageError = require('../GuiMessageError'); +var ValidationError = require('../../ValidationError'); var IdentifiedElement = require('../../map/data/IdentifiedElement'); // noinspection JSUnusedLocalSymbols @@ -71,7 +71,7 @@ ElementExportPanel.prototype.createResponseString = function () { var elements = []; return self.getSelectedTypes().then(function (result) { if (result.length === 0) { - return Promise.reject(new GuiMessageError("You must select at least one type")); + return Promise.reject(new ValidationError("You must select at least one type")); } types = result; return self.getSelectedIncludedCompartments(); @@ -108,7 +108,7 @@ ElementExportPanel.prototype.createResponseString = function () { return self.getSelectedColumns(); }).then(function (selectedColumns) { if (selectedColumns.length === 0) { - return Promise.reject(new GuiMessageError("You must select at least one column")); + return Promise.reject(new ValidationError("You must select at least one column")); } var rowPromises = []; diff --git a/frontend-js/src/main/js/gui/export/NetworkExportPanel.js b/frontend-js/src/main/js/gui/export/NetworkExportPanel.js index cc5941f29795fe8e83edca8eeb1b1e534ecc4edf..4f84e0ead68ee1c8aa115fb2fb009faa4ccf6f18 100644 --- a/frontend-js/src/main/js/gui/export/NetworkExportPanel.js +++ b/frontend-js/src/main/js/gui/export/NetworkExportPanel.js @@ -8,7 +8,7 @@ var Promise = require("bluebird"); // noinspection JSUnusedLocalSymbols var logger = require('../../logger'); var Functions = require('../../Functions'); -var GuiMessageError = require('../GuiMessageError'); +var ValidationError = require('../../ValidationError'); var Alias = require('../../map/data/Alias'); /** @@ -167,13 +167,13 @@ NetworkExportPanel.prototype.createResponseString = function () { var reactionTypesDiv = $("[name='reactionTypes']", self.getElement())[0]; return self.getSelectedTypes(elementTypesDiv).then(function (result) { if (result.length === 0) { - return Promise.reject(new GuiMessageError("You must select at least one element type")); + return Promise.reject(new ValidationError("You must select at least one element type")); } elementTypes = result; return self.getSelectedTypes(reactionTypesDiv); }).then(function (result) { if (result.length === 0) { - return Promise.reject(new GuiMessageError("You must select at least one reaction type")); + return Promise.reject(new ValidationError("You must select at least one reaction type")); } reactionTypes = result; return self.getSelectedIncludedCompartments(); @@ -221,7 +221,7 @@ NetworkExportPanel.prototype.createResponseString = function () { return self.getSelectedColumns(); }).then(function (selectedColumns) { if (selectedColumns.length === 0) { - return Promise.reject(new GuiMessageError("You must select at least one column")); + return Promise.reject(new ValidationError("You must select at least one column")); } var rowPromises = []; diff --git a/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js b/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js index 4df361a694d393162ca18c47db1ed84531c3f2d1..fe77c84072e9c6c8377433f09e86a32ea0a92f55 100644 --- a/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js +++ b/frontend-js/src/main/js/gui/leftPanel/GuiUtils.js @@ -402,21 +402,22 @@ GuiUtils.prototype.createAnnotationList = function (annotations, options) { } else { var row = document.createElement("div"); - row.style.height = "26px"; if (j % 2 === 0) { row.className = "minerva-annotation-row-odd"; } else { row.className = "minerva-annotation-row-even"; } - var header = document.createElement("div"); - header.style.width = "28px"; - header.style.float = "left"; - header.innerHTML = "[" + cntAnnotations + "]"; - row.appendChild(header); + row.appendChild(Functions.createElement({ + type: "div", + className: "minerva-annotation-counter", + content: "[" + cntAnnotations + "]" + })); - var body = document.createElement("div"); - body.style.float = "left"; + var body = Functions.createElement({ + type: "div", + className: "minerva-annotation-body" + }); body.appendChild(link); row.appendChild(body); groupContainer.appendChild(row); @@ -814,7 +815,7 @@ GuiUtils.prototype.createAliasElement = function (params) { modelId: alias.getModelId() }), true).then(function (compartment) { div.appendChild(self.createParamLine({ - label: "Compartment: ", value: compartment.getName(), + label: compartment.getType() + ": ", value: compartment.getName(), className: self._configurationOptionToClassName(ConfigurationType.SHOW_ELEMENT_COMPARTMENT) })); }) @@ -1139,6 +1140,23 @@ GuiUtils.prototype.hideTab = function (abstractGuiElement, panel) { } }; +/** + * + * @param {AbstractGuiElement} abstractGuiElement + * @param {Panel|HTMLElement} panel + */ +GuiUtils.prototype.removeTab = function (abstractGuiElement, panel) { + var panelId = getPanelId(panel); + var liElement = $("li:has(a[href='#" + panelId + "'])", $(abstractGuiElement.getElement()))[0]; + var contentElement = $("#" + panelId, $(abstractGuiElement.getElement()))[0]; + if (liElement !== undefined && contentElement !== undefined) { + liElement.parentElement.removeChild(liElement); + contentElement.parentElement.removeChild(contentElement); + } else { + logger.warn("Cannot find tab for panel: " + panel); + } +}; + /** * * @param {AbstractGuiElement} abstractGuiElement diff --git a/frontend-js/src/main/js/gui/leftPanel/OverlayPanel.js b/frontend-js/src/main/js/gui/leftPanel/OverlayPanel.js index eadedffd74d986e72867b839cb675598dfada936..3cfbaf61a480d9a3a938aac318389aefc0dfb5f9 100644 --- a/frontend-js/src/main/js/gui/leftPanel/OverlayPanel.js +++ b/frontend-js/src/main/js/gui/leftPanel/OverlayPanel.js @@ -458,6 +458,7 @@ OverlayPanel.prototype.refresh = function (showDefault) { creator: user.getLogin() }); }).then(function (customOverlays) { + self.getProject().addOrUpdateDataOverlays(customOverlays); if (!showDefault) { var id = self.getMap().getBackgroundDataOverlay().getId(); @@ -485,6 +486,22 @@ OverlayPanel.prototype.refresh = function (showDefault) { var body = document.createElement("tbody"); table.appendChild(body); + + generalOverlays.sort(function (o1, o2) { + var val1 = o1.getId(); + var val2 = o2.getId(); + if (o1.getOrder() !== o2.getOrder()) { + val1 = o1.getOrder(); + val2 = o2.getOrder(); + } + if (val1 < val2) { + return -1; + } + if (val1 > val2) { + return 1; + } + return 0; + }); for (i = 0; i < generalOverlays.length; i++) { overlay = generalOverlays[i]; body.appendChild(self.createOverlayRow(overlay, selectedOverlay[overlay.getId()], false)); diff --git a/frontend-js/src/main/js/gui/topMenu/TopMenu.js b/frontend-js/src/main/js/gui/topMenu/TopMenu.js index 9de0a275d327fbb32a9ca04fbb65920886d8958f..7a49e91561726e8c1a722b21bac322d65a364624 100644 --- a/frontend-js/src/main/js/gui/topMenu/TopMenu.js +++ b/frontend-js/src/main/js/gui/topMenu/TopMenu.js @@ -181,6 +181,7 @@ TopMenu.prototype.init = function () { } else { icon.className = "fa fa-chevron-left"; self.getLeftPanel().show(); + $(".minerva-panel").each(onresize); } self.getMap().getMapCanvas().triggerListeners('resize'); }; diff --git a/frontend-js/src/main/js/map/CustomMap.js b/frontend-js/src/main/js/map/CustomMap.js index 62c307c332cb806abb5ea24da72ea0d6a8b598e8..4577413f85a47b22333748cb3286cd5424f6b8b0 100644 --- a/frontend-js/src/main/js/map/CustomMap.js +++ b/frontend-js/src/main/js/map/CustomMap.js @@ -122,16 +122,14 @@ CustomMap.prototype.init = function () { return Promise.each(ids, function (overlayId) { - try { - return self.openDataOverlay(overlayId); - } catch (e) { + return self.openDataOverlay(overlayId).catch(function (e) { if (e instanceof SecurityError) { logger.debug(e.message); sessionData.setSelectedBackgroundOverlay(self.getProject().getDataOverlays()[0].getId()); } else { return Promise.reject(e); } - } + }); }); }).then(function () { var logo2 = self.getControl(ControlType.LOGO_2_IMG); @@ -224,7 +222,7 @@ CustomMap.prototype.openDataOverlay = function (param) { identifier = parseInt(identifier); if (isNaN(identifier)) { - throw new Error("invalid id: " + param); + return Promise.reject(new Error("invalid id: " + param)); } var overlayToOpen = null; @@ -237,7 +235,7 @@ CustomMap.prototype.openDataOverlay = function (param) { } if (overlayToOpen === null) { - throw new SecurityError("You have no privileges for selected overlay"); + return Promise.reject(new SecurityError("You have no privileges for selected overlay")); } else { return overlayToOpen.init().then(function () { if (overlayToOpen.getInputDataAvailable()) { @@ -1237,7 +1235,7 @@ CustomMap.prototype.getDistance = function (params) { var elementWidth = element.getWidth(); var elementHeight = element.getHeight(); return Math.min( - Functions.distance(p1, new Point(elementX, y)), + Functions.distance(p1, new Point(elementX, elementY)), Functions.distance(p1, new Point(elementX + elementWidth, elementY)), Functions.distance(p1, new Point(elementX, elementY + elementHeight)), Functions.distance(p1, new Point(elementX + elementWidth, elementY + elementHeight)) diff --git a/frontend-js/src/main/js/map/data/PluginData.js b/frontend-js/src/main/js/map/data/PluginData.js index 3f2b82c391aa5834fcbb1165301595e4d3b7f9e3..a2d3c224777835d4ade3a3beaadec1c089343901 100644 --- a/frontend-js/src/main/js/map/data/PluginData.js +++ b/frontend-js/src/main/js/map/data/PluginData.js @@ -1,5 +1,7 @@ "use strict"; +var Functions = require('../../Functions'); + /** * * @param {Object} params @@ -36,11 +38,13 @@ PluginData.prototype.getHash = function () { /** * - * @param {boolean} isPublic + * @param {string|boolean} isPublic */ PluginData.prototype.setPublic = function (isPublic) { if (isPublic === null || isPublic === undefined) { this._public = false; + } else if (Functions.isString(isPublic)) { + this._public = (isPublic === "true"); } else { this._public = isPublic; } diff --git a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js index 8f5e46dbf98aebf8616772b197cb3090b97607b6..5f1608fb7887d8c34acdb69ee2c87f43fb7a2136 100644 --- a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js +++ b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js @@ -98,28 +98,39 @@ SearchDbOverlay.prototype.getElementsByQuery = function (query) { /** * * @param {Point} coordinates - * @param {IdentifiedElement} compartment + * @param {IdentifiedElement[]} compartments * @param {number} maxDistance * @returns {Promise} * @private */ -SearchDbOverlay.prototype._returnCompartmentIfClickOnBorder = function (coordinates, compartment, maxDistance) { - var model = this.getMap().getSubmapById(compartment.getModelId()).getModel(); - - return model.getByIdentifiedElement(compartment).then(function (result) { - var p1 = new Point(result.getX(), result.getY()); - var p2 = new Point(result.getX() + result.getWidth(), result.getY()); - var p3 = new Point(result.getX() + result.getWidth(), result.getY() + result.getHeight()); - var p4 = new Point(result.getX(), result.getY() + result.getHeight()); - var distance = Functions.distance(coordinates, {start: p1, end: p2}); - distance = Math.min(distance, Functions.distance(coordinates, {start: p2, end: p3})); - distance = Math.min(distance, Functions.distance(coordinates, {start: p3, end: p4})); - distance = Math.min(distance, Functions.distance(coordinates, {start: p4, end: p1})); - - if (maxDistance>distance) { - return compartment; - } else { - return undefined; +SearchDbOverlay.prototype._returnCompartmentIfClickOnBorder = function (coordinates, compartments, maxDistance) { + var promises = []; + for (var i = 0; i < compartments.length; i++) { + var compartment = compartments[i]; + var model = this.getMap().getSubmapById(compartment.getModelId()).getModel(); + promises.push(model.getByIdentifiedElement(compartment).then(function (result) { + var p1 = new Point(result.getX(), result.getY()); + var p2 = new Point(result.getX() + result.getWidth(), result.getY()); + var p3 = new Point(result.getX() + result.getWidth(), result.getY() + result.getHeight()); + var p4 = new Point(result.getX(), result.getY() + result.getHeight()); + var distance = Functions.distance(coordinates, {start: p1, end: p2}); + distance = Math.min(distance, Functions.distance(coordinates, {start: p2, end: p3})); + distance = Math.min(distance, Functions.distance(coordinates, {start: p3, end: p4})); + distance = Math.min(distance, Functions.distance(coordinates, {start: p4, end: p1})); + + if (maxDistance > distance) { + return new IdentifiedElement(result); + } else { + return undefined; + } + }) + ); + } + return Promise.all(promises).then(function (result) { + for (var i = 0; i < result.length; i++) { + if (result[i] !== undefined) { + return result[i]; + } } }); }; @@ -144,7 +155,7 @@ SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedE //transparent compartments shouldn't be clickable if (fullElement.getTransparencyLevel() <= zoomLevel && fullElement.getType() === "Compartment") { var maxDistance = self.computeMaxDistance(model, zoomLevel); - return self._returnCompartmentIfClickOnBorder(coordinates, identifiedElement, maxDistance); + return self._returnCompartmentIfClickOnBorder(coordinates, [identifiedElement], maxDistance); } } return identifiedElement; @@ -202,18 +213,20 @@ SearchDbOverlay.prototype.findCompartmentByCoordinates = function (coordinates, return ServerConnector.getClosestElementsByCoordinates({ modelId: model.getId(), coordinates: coordinates, - count: 1, - type: ["Compartment"] + count: 10, + type: ["Compartment", "Pathway"] }).then(function (elements) { - var nestedOverlay = "Pathways and compartments"; if (elements.length === 0) { return undefined; } else { - if (self.getMap().getBackgroundDataOverlay().getName() === nestedOverlay) { - return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom(), coordinates); - } else { - return self._returnCompartmentIfClickOnBorder(coordinates, elements[0], maxDistance); - } + return self._returnCompartmentIfClickOnBorder(coordinates, elements, maxDistance).then(function (result) { + var nestedOverlay = "Pathways and compartments"; + if (result === undefined && self.getMap().getBackgroundDataOverlay().getName() === nestedOverlay) { + return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom(), coordinates); + } else { + return result; + } + }); } }); }; diff --git a/frontend-js/src/main/js/minerva.js b/frontend-js/src/main/js/minerva.js index 9337a2484105b494ed89eb880d46256fedf52eda..4c3a1d5f827921fc172e7ad8ee02ff9678399a37 100644 --- a/frontend-js/src/main/js/minerva.js +++ b/frontend-js/src/main/js/minerva.js @@ -23,12 +23,14 @@ var SelectionContextMenu = require('./gui/SelectionContextMenu'); var GuiConnector = require('./GuiConnector'); var ServerConnector = require('./ServerConnector'); var SecurityError = require('./SecurityError'); +var ValidationError = require('./ValidationError'); var MolArt = require('./map/structure/MolArt'); var Promise = require("bluebird"); var logger = require('./logger'); +var Functions = require('./Functions'); var customMap, leftPanel, topMenu, legend, mapContextMenu, selectionContextMenu; @@ -63,7 +65,16 @@ function processUrlGetParams(params) { } if (GuiConnector.getParams["overlays"] !== undefined) { - sessionData.setVisibleOverlays(GuiConnector.getParams["overlays"].split(",")); + var overlays = GuiConnector.getParams["overlays"].split(","); + var data = []; + for (var i = 0; i < overlays.length; i++) { + if (Functions.isInt(parseInt(overlays[i]))) { + data.push(overlays[i]); + } else { + GuiConnector.warn("Invalid overlay id: " + overlays[i]); + } + } + sessionData.setVisibleOverlays(data); } if (GuiConnector.getParams["comments"] === "on") { @@ -479,7 +490,7 @@ function create(params) { if (project === null) { var message = "Project with given id doesn't exist."; message += "<p>Please go to <a href='" + ServerConnector.getServerBaseUrl() + "'>default map</a>"; - return Promise.reject(new Error(message)); + return Promise.reject(new ValidationError(message)); } params.setProject(project); var promise = Promise.resolve(); diff --git a/frontend-js/src/main/js/plugin/MinervaPluginProxy.js b/frontend-js/src/main/js/plugin/MinervaPluginProxy.js index 32d295284650e715ba1f3d84215684f79e760522..691b8986329af6f7f17f8005aedbfd7f5646745d 100644 --- a/frontend-js/src/main/js/plugin/MinervaPluginProxy.js +++ b/frontend-js/src/main/js/plugin/MinervaPluginProxy.js @@ -535,6 +535,7 @@ function createProjectMap(options) { listenerData.object.removeListener(listenerData.type, listenerWrapper); removedListeners.push(listenerData.listener); } + listenersData = []; return removedListeners; }, getHighlightedBioEntities: function (dbOverlayName) { diff --git a/frontend-js/src/main/js/plugin/Plugin.js b/frontend-js/src/main/js/plugin/Plugin.js index 94d44b2662a84803c43b79f6c888d601e66d248d..409d9d68c786c6a3d4c699f794906bad21fd5e82 100644 --- a/frontend-js/src/main/js/plugin/Plugin.js +++ b/frontend-js/src/main/js/plugin/Plugin.js @@ -80,7 +80,7 @@ Plugin.prototype.getLoadedPluginData = function () { /** * - * @param minervaPluginProxy + * @param {MinervaPluginProxy} minervaPluginProxy */ Plugin.prototype.setMinervaPluginProxy = function (minervaPluginProxy) { this._minervaPluginProxy = minervaPluginProxy; @@ -88,7 +88,7 @@ Plugin.prototype.setMinervaPluginProxy = function (minervaPluginProxy) { /** * - * @returns {*} + * @returns {MinervaPluginProxy} */ Plugin.prototype.getMinervaPluginProxy = function () { return this._minervaPluginProxy; diff --git a/frontend-js/src/main/js/plugin/PluginManager.js b/frontend-js/src/main/js/plugin/PluginManager.js index 201c5a8993299c474949b75044e3e3927343c3d6..c1b60b467e964c61ebf6e7a3547630cd62df0a2d 100644 --- a/frontend-js/src/main/js/plugin/PluginManager.js +++ b/frontend-js/src/main/js/plugin/PluginManager.js @@ -97,10 +97,7 @@ PluginManager.prototype.addPlugin = function (options) { serverConnector: self.getServerConnector() }); plugin.addListener("onUnload", function () { - self.getGuiUtils().hideTab(self, element); - if ($("li", self.getElement()).children(':visible').length === 0) { - $(self.getElement()).hide(); - } + self.getGuiUtils().removeTab(self, element); }); if (!self.isValidUrl(options.url)) { return Promise.reject(new InvalidArgumentError("url: '" + options.url + "' is invalid")); @@ -189,6 +186,7 @@ PluginManager.prototype.removePlugin = function (plugin) { GuiConnector.removeWindowResizeEvent(self._pluginOnResizeHandlers[plugin.getPluginId()]); return plugin.unload().then(function () { if (self._plugins.length === 0) { + $(self.getElement()).hide(); return self.getMap().getMapCanvas().triggerListeners("resize"); } else { return self.adjustMinWidth(); diff --git a/frontend-js/src/test/js/gui/admin/AddPluginDialog-test.js b/frontend-js/src/test/js/gui/admin/AddPluginDialog-test.js index 2275ecb1e0cc75017ba015f8b28a59df1584fea0..6956ab3cbb116a7b14e98b3c198ca7cd519d17fd 100644 --- a/frontend-js/src/test/js/gui/admin/AddPluginDialog-test.js +++ b/frontend-js/src/test/js/gui/admin/AddPluginDialog-test.js @@ -5,6 +5,7 @@ require("../../mocha-config"); var AddPluginDialog = require('../../../../main/js/gui/admin/AddPluginDialog'); var ServerConnector = require('../../ServerConnector-mock'); var ValidationError = require('../../../../main/js/ValidationError'); +var PluginData = require('../../../../main/js/map/data/PluginData'); var logger = require('../../logger'); @@ -26,6 +27,12 @@ describe('AddPluginDialog', function () { serverConnector: ServerConnector }); }; + var getPluginsData = ServerConnector.getPluginsData; + + afterEach(function () { + ServerConnector.getPluginsData = getPluginsData; + }); + it('init', function () { helper.loginAsAdmin(); var dialog = createDialog(); @@ -85,8 +92,35 @@ describe('AddPluginDialog', function () { return dialog.destroy(); }); }); + it('valid file exists', function () { + helper.loginAsAdmin(); + var dialog = createDialog(); + ServerConnector.getPluginsData = function () { + return Promise.resolve([new PluginData({urls: "./testFiles/plugin/empty.js", isPublic: "true"})]); + }; + return dialog.init().then(function () { + $("[name='pluginUrl']").val("./testFiles/plugin/empty.js"); + return dialog.onValidateClicked(); + }).then(function () { + assert.notOk("error expected"); + }).catch(function (e) { + assert.ok(e instanceof ValidationError); + }).finally(function () { + return dialog.destroy(); + }); + }); + it('valid file exists as a private plugin', function () { + helper.loginAsAdmin(); + var dialog = createDialog(); + ServerConnector.getPluginsData = function () { + return Promise.resolve([new PluginData({urls: "./testFiles/plugin/empty.js", isPublic: "false"})]); + }; + return dialog.init().then(function () { + $("[name='pluginUrl']").val("./testFiles/plugin/empty.js"); + return dialog.onValidateClicked(); + }).finally(function () { + return dialog.destroy(); + }); + }); }); - - -}) -; +}); 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 ee2ae9c4c09b824959e0a7cfddc149688882ef35..081fa51d122985ba54ad29e0bee79dac746217d4 100644 --- a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js +++ b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js @@ -89,6 +89,18 @@ describe('AddProjectDialog', function () { return dialog.destroy(); }); }); + it('filename should be the same', function () { + var filename = "complex_model_with_submaps.zip"; + var dialog = createDialog(); + var buf = fs.readFileSync("testFiles/map/" + filename); + buf.name = filename; + return dialog.init().then(function () { + return dialog.setZipFileContent(buf); + }).then(function () { + assert.equal(filename, dialog.getFilename()); + return dialog.destroy(); + }); + }); it('overlays', function () { var dialog = createDialog(); var buf = fs.readFileSync("testFiles/map/complex_model_with_overlays.zip"); @@ -131,7 +143,7 @@ describe('AddProjectDialog', function () { }; var dialog = createDialog(); var projectAdded = false; - dialog.addListener("onProjectAdd", function(){ + dialog.addListener("onProjectAdd", function () { projectAdded = true; }); diff --git a/frontend-js/src/test/js/gui/export/ElementExportPanel-test.js b/frontend-js/src/test/js/gui/export/ElementExportPanel-test.js index 65a91982efe8e56d9ee6a180ed2f6908c7b190f6..2ae3767818c37ff3c62881bfefd0c14cc7bff44a 100644 --- a/frontend-js/src/test/js/gui/export/ElementExportPanel-test.js +++ b/frontend-js/src/test/js/gui/export/ElementExportPanel-test.js @@ -3,7 +3,7 @@ require("../../mocha-config.js"); var ElementExportPanel = require('../../../../main/js/gui/export/ElementExportPanel'); -var GuiMessageError = require('../../../../main/js/gui/GuiMessageError'); +var ValidationError = require('../../../../main/js/ValidationError'); var MiriamType = require('../../../../main/js/map/data/MiriamType'); var ServerConnector = require('../../ServerConnector-mock'); var logger = require('../../logger'); @@ -197,7 +197,7 @@ describe('ElementExportPanel', function () { }).then(function () { return exportObject.createResponseString(); }).then(null, function (error) { - assert.ok(error instanceof GuiMessageError); + assert.ok(error instanceof ValidationError); }); }); }); diff --git a/frontend-js/src/test/js/gui/leftPanel/GuiUtils-test.js b/frontend-js/src/test/js/gui/leftPanel/GuiUtils-test.js index b3647262a2846b5deb0e7b4868c4dc6b216b181a..1ad333b042c3e591b1b2e305e1e711f81eab30bc 100644 --- a/frontend-js/src/test/js/gui/leftPanel/GuiUtils-test.js +++ b/frontend-js/src/test/js/gui/leftPanel/GuiUtils-test.js @@ -166,11 +166,14 @@ describe('GuiUtils', function () { var guiUtils = createGuiUtils(map); var alias = helper.createAlias(map); var compartment = helper.createAlias(map); + compartment.setName("compartment_name"); + compartment.setType("XYZ"); + alias.setCompartmentId(compartment.getId()); - compartment.setName("compartment_name"); return guiUtils.createAliasElement({alias: alias}).then(function (div) { - assert.ok(div.innerHTML.indexOf("compartment_name") >= 0); + assert.ok(div.innerHTML.indexOf(compartment.getName()) >= 0); + assert.ok(div.innerHTML.indexOf(compartment.getType()) >= 0); }); }); diff --git a/frontend-js/src/test/js/map/CustomMap-test.js b/frontend-js/src/test/js/map/CustomMap-test.js index 3f1508a87d02289b5ec16f50bcfb2fb1fafc64bf..17f110957e8d56789855869f030a5623491a64ee 100644 --- a/frontend-js/src/test/js/map/CustomMap-test.js +++ b/frontend-js/src/test/js/map/CustomMap-test.js @@ -70,12 +70,11 @@ describe('CustomMap', function () { describe("openDataOverlay", function () { it("for not existing id", function () { var map = helper.createCustomMap(); - try { - map.openDataOverlay(-1); + return map.openDataOverlay(-1).then(function () { assert.ok(false); - } catch (exception) { + }).catch(function (exception) { assert.ok(exception.message.indexOf("You have no privileges") >= 0); - } + }); }); it("for int id", function () { @@ -1018,4 +1017,84 @@ describe('CustomMap', function () { return map.getControl(ControlType.LOGO_IMG).onclick() }); }); + describe("getDistance", function () { + it("inside element", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(100,200), element: ie}).then(function(distance){ + assert.equal(0, distance); + return map.destroy(); + }); + }); + it("to the left", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(1,200), element: ie}).then(function(distance){ + assert.equal(49, distance); + return map.destroy(); + }); + }); + it("to the right", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(161,200), element: ie}).then(function(distance){ + assert.equal(11, distance); + return map.destroy(); + }); + }); + it("to the top", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(100,11), element: ie}).then(function(distance){ + assert.equal(49, distance); + return map.destroy(); + }); + }); + it("to the bottom", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(100,300), element: ie}).then(function(distance){ + assert.equal(40, distance); + return map.destroy(); + }); + }); + it("corner", function () { + var map = helper.createCustomMap(); + var alias = helper.createAlias(map); + alias.setX(50); + alias.setY(60); + alias.setWidth(100); + alias.setHeight(200); + var ie = new IdentifiedElement(alias); + return map.getDistance({modelId:map.getId(), coordinates: new Point(46,57), element: ie}).then(function(distance){ + assert.equal(5, distance); + return map.destroy(); + }); + }); + }); }); diff --git a/frontend-js/src/test/js/minerva-test.js b/frontend-js/src/test/js/minerva-test.js index b1d38ce2161efa9ddada4df3478ae848da47fc41..9eecc4932fecee98e551462bf7d282bdf3751f4a 100644 --- a/frontend-js/src/test/js/minerva-test.js +++ b/frontend-js/src/test/js/minerva-test.js @@ -127,7 +127,7 @@ describe('minerva global', function () { }); }); - describe('create with overlay', function () { + describe('create with layout', function () { it('background as layout param in URL', function () { var overlay, project, plugin, map; return ServerConnectorMock.getProject().then(function (result) { @@ -191,6 +191,29 @@ describe('minerva global', function () { }); }); + describe('create with overlays', function () { + it('invalid overlay as param in URL', function () { + helper.setUrl("http://test/?overlays=xxx"); + return ServerConnectorMock.getProject().then(function (project) { + var options = helper.createCustomMapOptions(project); + return minerva.create(options); + }).then(function (result) { + assert.equal(1, logger.getWarnings().length); + return result.destroy(); + }); + }); + it('valid overlay as param in URL', function () { + helper.setUrl("http://test/?overlays=1"); + return ServerConnectorMock.getProject().then(function (project) { + var options = helper.createCustomMapOptions(project); + return minerva.create(options); + }).then(function (result) { + assert.equal(0, logger.getWarnings().length); + return result.destroy(); + }); + }); + }); + it('create restricted map', function () { var originalFunction = ServerConnectorMock.getProject; ServerConnectorMock.getProject = function () { diff --git a/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js b/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js index 3fe725e9187c3368507b6eba9ca1422500601f2e..3edb4540c4f8a39f637a450e1c5561aafb60225a 100644 --- a/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js +++ b/frontend-js/src/test/js/plugin/MinervaPluginProxy-test.js @@ -166,6 +166,23 @@ describe('MinervaPluginProxy', function () { return map.destroy(); }); }); + it('call twice', function () { + var map = helper.createCustomMap(); + var proxy = createProxy(map); + + var options = { + object: "map", + type: "onZoomChanged", + callback: function () { + } + }; + proxy.project.map.addListener(options); + var removedListeners = proxy.project.map.removeAllListeners(); + assert.equal(1, removedListeners.length); + removedListeners = proxy.project.map.removeAllListeners(); + assert.equal(0, removedListeners.length); + return map.destroy(); + }); it('no listeners', function () { var map; return ServerConnector.getProject().then(function (project) { @@ -688,8 +705,8 @@ describe('MinervaPluginProxy', function () { helper.createSearchDbOverlay(map); proxy = createProxy(map); var overlays = proxy.project.data.getDataOverlays(); - assert.ok(overlays.length>0); - for (var i=0;i<overlays.length;i++) { + assert.ok(overlays.length > 0); + for (var i = 0; i < overlays.length; i++) { assert.ok(overlays[i].getInputDataAvailable()); } return map.destroy(); diff --git a/frontend-js/src/test/js/plugin/PluginManager-test.js b/frontend-js/src/test/js/plugin/PluginManager-test.js index e251c7d48b0074f7ad75b41f8103787b4b27147b..b8ff7f823d3d0c7d1a40b8ba183cb32c652d595a 100644 --- a/frontend-js/src/test/js/plugin/PluginManager-test.js +++ b/frontend-js/src/test/js/plugin/PluginManager-test.js @@ -122,6 +122,7 @@ describe('PluginManager', function () { return manager.removePlugin(plugin); }).then(function () { assert.equal(0, manager.getPlugins().length); + assert.equal(-1, testDiv.innerHTML.indexOf("tab-pane")); }); }); it('removing non existing plugin', function () { diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/frontend-js/testFiles/apiCalls/projects/empty/models/20638/bioEntities.search/coordinates=553.10,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/empty/models/20638/bioEntities.search/coordinates=553.10,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/empty/models/20638/bioEntities.search/coordinates=553.10,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/empty/models/20638/bioEntities.search/coordinates=553.10,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=184.79,365.76&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=184.79,365.76&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=184.79,365.76&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=184.79,365.76&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=207.73,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=207.73,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=207.73,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=207.73,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=316.05,253.61&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=316.05,253.61&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=316.05,253.61&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=316.05,253.61&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=553.10,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=553.10,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& similarity index 100% rename from frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=553.10,479.18&count=1&type=Compartment&token=MOCK_TOKEN_ID& rename to frontend-js/testFiles/apiCalls/projects/sample/models/15781/bioEntities.search/coordinates=553.10,479.18&count=10&type=Compartment,Pathway&token=MOCK_TOKEN_ID& diff --git a/model/src/main/java/lcsb/mapviewer/model/map/compartment/PathwayCompartment.java b/model/src/main/java/lcsb/mapviewer/model/map/compartment/PathwayCompartment.java index 9faeecc187da386aadfe4587df7189ba150d8edd..2c1fe73421a8c20ce8f7dd4302221fb418b16dda 100644 --- a/model/src/main/java/lcsb/mapviewer/model/map/compartment/PathwayCompartment.java +++ b/model/src/main/java/lcsb/mapviewer/model/map/compartment/PathwayCompartment.java @@ -60,4 +60,8 @@ public class PathwayCompartment extends Compartment { } } + @Override + public String getStringType() { + return "Pathway"; + } } diff --git a/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java b/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java index e0d5a159017292f0d8a122f78779f1c97ae08b8b..aaaca22b66b49855f8d19a5ce01736bb59165054 100644 --- a/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java +++ b/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java @@ -184,13 +184,13 @@ public enum ConfigurationElementType { ConfigurationElementEditType.TEXT, false, ConfigurationElementTypeGroup.EMAIL_NOTIFICATION), DEFAULT_VIEW_PROJECT("Default user privilege for: " + PrivilegeType.VIEW_PROJECT.getCommonName(), "true", - ConfigurationElementEditType.BOOLEAN, true, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), + ConfigurationElementEditType.BOOLEAN, false, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), DEFAULT_EDIT_COMMENTS_PROJECT("Default user privilege for: " + PrivilegeType.EDIT_COMMENTS_PROJECT.getCommonName(), - "false", ConfigurationElementEditType.BOOLEAN, true, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), + "false", ConfigurationElementEditType.BOOLEAN, false, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), DEFAULT_LAYOUT_MANAGEMENT("Default user privilege for: " + PrivilegeType.LAYOUT_MANAGEMENT.getCommonName(), "false", - ConfigurationElementEditType.BOOLEAN, true, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), + ConfigurationElementEditType.BOOLEAN, false, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), SHOW_REACTION_TYPE("Show reaction type", "true", ConfigurationElementEditType.BOOLEAN, false, ConfigurationElementTypeGroup.SEARCH_VISIBLE_PARAMETERS), @@ -232,7 +232,7 @@ public enum ConfigurationElementType { ConfigurationElementEditType.STRING, true, ConfigurationElementTypeGroup.LDAP_CONFIGURATION), DEFAULT_CUSTOM_LAYOUTS("Default user privilege for: " + PrivilegeType.CUSTOM_LAYOUTS.getCommonName(), "0", - ConfigurationElementEditType.INTEGER, true, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), + ConfigurationElementEditType.INTEGER, false, ConfigurationElementTypeGroup.DEFAULT_USER_PRIVILEGES), LDAP_UID("LDAP login (user id)", "uid", ConfigurationElementEditType.STRING, true, ConfigurationElementTypeGroup.LDAP_CONFIGURATION), diff --git a/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190221__uploaded_zip_project_file_rename.sql b/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190221__uploaded_zip_project_file_rename.sql new file mode 100644 index 0000000000000000000000000000000000000000..9125ca61e930c69b49f442e9ebc865bd58d43217 --- /dev/null +++ b/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190221__uploaded_zip_project_file_rename.sql @@ -0,0 +1 @@ +update file_entry_table set original_file_name = concat(original_file_name,'.zip') where id in (select file_entry_id from project_table where id in (select project_id from model_data_table where id in (select distinct(parent_model_id) from submodel_connection_table where parent_model_id is not null ))) and not original_file_name like '%zip'; diff --git a/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190222__layout_table_should_not_have_model_id.sql b/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190222__layout_table_should_not_have_model_id.sql new file mode 100644 index 0000000000000000000000000000000000000000..59c78d4fb1f086ef71629d72ad1b60efc2977227 --- /dev/null +++ b/persist/src/main/resources/db/migration/12.2.0~beta.2/V12.2.0.20190222__layout_table_should_not_have_model_id.sql @@ -0,0 +1 @@ +alter table layout_table drop column model_id; \ No newline at end of file diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java index de0b820aa8092cf9556a79cb574c5519c72667b3..44bac7681ebb183629407e94e2bc9f52ac4e89c7 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java @@ -134,24 +134,9 @@ public abstract class BaseRestImpl { result.put("id", annotation.getId()); if (annotation.getAnnotator() != null) { - try { - result.put("annotatorClassName", annotation.getAnnotator().getName()); - result.put("descriptionByType", ((ElementAnnotator) annotation.getAnnotator().getConstructor().newInstance()) - .getDescription(annotation.getDataType())); - result.put("descriptionByTypeRelation", - ((ElementAnnotator) annotation.getAnnotator().getConstructor().newInstance()) - .getDescription(annotation.getDataType(), annotation.getRelationType())); - - } catch (Exception e) { - logger.error("Problem with retrieving description from annotator", e); - result.put("annotatorClassName", ""); - result.put("descriptionByType", ""); - result.put("descriptionByTypeRelation", ""); - } + result.put("annotatorClassName", annotation.getAnnotator().getName()); } else { result.put("annotatorClassName", ""); - result.put("descriptionByType", ""); - result.put("descriptionByTypeRelation", ""); } return result; 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 509bdc38d5ca93d16b9955e419ea38ee79d4aafb..2093eee10dcab2859c563657cc31e1890ffb4037 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 @@ -503,6 +503,9 @@ public class ProjectRestImpl extends BaseRestImpl { public Map<String, Object> removeProject(String token, String projectId, String path) throws SecurityException, QueryException { Project project = getProjectService().getProjectByProjectId(projectId, token); + if (project == null) { + throw new ObjectNotFoundException("Project with given id doesn't exist"); + } if (getConfigurationService().getConfigurationValue(ConfigurationElementType.DEFAULT_MAP) .equals(project.getProjectId())) { throw new OperationNotAllowedException("You cannot remove default map"); diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java b/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java index e8f49bd81b9668101a02ba8d7d831d1af34db5f6..086d8bc3ecd8932cc54d5ce796b807c7d9636d94 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java @@ -154,9 +154,10 @@ public class UserController extends BaseController { if (auth != null) { new SecurityContextLogoutHandler().logout(request, response, auth); } + //for some reason spring doesn't invalidate sessionRegistry data on logout + userService.logout(token); Map<String, Object> result = new TreeMap<>(); - result.put("status", "OK"); final Boolean useSecureCookie = false; final String cookiePath = "/"; 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 62697f3977247246e7cae238bc4f136804712906..0bcf3cf3dcee4d445b14c2ee2d5821d9b8087db7 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 @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import org.apache.log4j.Logger; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; @@ -50,6 +51,12 @@ public class ProjectRestImplTest extends RestTestFunctions { @Autowired ProjectDao projectDao; + @Before + public void before() { + _projectRestImpl.setModelService(modelService); + _projectRestImpl.setProjectService(projectService); + + } @Test public void testGetModelDataDependencies() throws Exception { try { @@ -64,6 +71,16 @@ public class ProjectRestImplTest extends RestTestFunctions { } } + @Test(expected = ObjectNotFoundException.class) + public void testRemoveNonExistentProject() throws Exception { + try { + _projectRestImpl.removeProject(adminToken, "blabla", null); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + @Test(expected = ObjectNotFoundException.class) public void testGetInvalidMetaData() throws Exception { ProjectRestImpl projectRest = createMockProjectRest(null); diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java index 7a5b8c0c13ac5c7ab354c8f0fd928699f237cf47..50acecfe1e036ca58799145fb5bdc73749163921 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java @@ -652,6 +652,10 @@ public class ProjectService implements IProjectService { submodelId++; } } + int order = 0; + for (Layout l : project.getLayouts()) { + l.setOrderIndex(order++); + } projectDao.update(project); if (params.isUpdateAnnotations()) { @@ -683,7 +687,7 @@ public class ProjectService implements IProjectService { Integer zoomLevels = generator.computeZoomLevels(topModel); if (zoomLevels > maxZoomLevels) { throw new InvalidInputDataExecption( - "Map " + topModel.getName() + " too big. You can change the max size of map in configuration."); + "Map " + topModel.getName() + " too big. You can change the max number of map zoom levels in configuration."); } topModel.setZoomLevels(zoomLevels); topModel.setTileSize(MapGenerator.TILE_SIZE); @@ -1202,9 +1206,8 @@ public class ProjectService implements IProjectService { Model model = modelService.getLastModelByProjectId(project.getProjectId(), token); if (model != null) { // TODO it's a hack to prevent lazy initialization of the project - project.setModels(model.getProject().getModels()); - project.setLayouts(model.getProject().getLayouts()); - project.setOverviewImages(model.getProject().getOverviewImages()); + project.getLayouts(); + project.getOverviewImages(); model.setProject(project); } } diff --git a/web/src/main/java/lcsb/mapviewer/web/bean/utils/ApiAccessControlFilter.java b/web/src/main/java/lcsb/mapviewer/web/bean/utils/ApiAccessControlFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..1d490fc98a6a2616d541f423891a4a250628782b --- /dev/null +++ b/web/src/main/java/lcsb/mapviewer/web/bean/utils/ApiAccessControlFilter.java @@ -0,0 +1,44 @@ +package lcsb.mapviewer.web.bean.utils; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; + +/** + * This filter enables x-frames from another domain if necessary. + * + * @author Piotr Gawron + * + */ +public class ApiAccessControlFilter implements Filter { + /** + * Default class logger. + */ + @SuppressWarnings("unused") + private final Logger logger = Logger.getLogger(ApiAccessControlFilter.class); + + @Override + public void init(FilterConfig config) throws ServletException { + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletResponse response = (HttpServletResponse) res; + response.addHeader("Vary", "*"); + chain.doFilter(req, response); + } + + @Override + public void destroy() { + } + +} diff --git a/web/src/main/java/lcsb/mapviewer/web/bean/utils/StartupBean.java b/web/src/main/java/lcsb/mapviewer/web/bean/utils/StartupBean.java index 2b16f5cbd3addf9819e7db9ca6253a8f7064c0bd..536011ba9a55409498d1c92008788289e7527a6d 100644 --- a/web/src/main/java/lcsb/mapviewer/web/bean/utils/StartupBean.java +++ b/web/src/main/java/lcsb/mapviewer/web/bean/utils/StartupBean.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; +import javax.annotation.PostConstruct; import javax.faces.context.FacesContext; import javax.servlet.ServletContext; @@ -73,7 +74,7 @@ public class StartupBean { /** * Method that process initial script of application. */ - @EventListener(ApplicationReadyEvent.class) + @PostConstruct public void init() { loadCustomLog4jProperties(); logger.debug("Application startup script starts"); diff --git a/web/src/main/java/lcsb/mapviewer/web/config/SpringWebConfig.java b/web/src/main/java/lcsb/mapviewer/web/config/SpringWebConfig.java index 2d829aff04191c94a38102615841660c4c6baaea..b483516ebc6ca3debd6796ebfff8cfb1bde4b852 100644 --- a/web/src/main/java/lcsb/mapviewer/web/config/SpringWebConfig.java +++ b/web/src/main/java/lcsb/mapviewer/web/config/SpringWebConfig.java @@ -1,10 +1,13 @@ package lcsb.mapviewer.web.config; -import lcsb.mapviewer.services.SpringServiceConfig; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import lcsb.mapviewer.services.SpringServiceConfig; @Configuration @EnableWebMvc diff --git a/web/src/main/java/lcsb/mapviewer/web/config/WebAppInitializer.java b/web/src/main/java/lcsb/mapviewer/web/config/WebAppInitializer.java index 931e9b49b0741ad8dd2066cecee2618e881ac464..cde1173065dbf35895da25efe6341549c8b720d3 100644 --- a/web/src/main/java/lcsb/mapviewer/web/config/WebAppInitializer.java +++ b/web/src/main/java/lcsb/mapviewer/web/config/WebAppInitializer.java @@ -2,12 +2,9 @@ package lcsb.mapviewer.web.config; import java.io.InputStream; import java.util.Arrays; -import java.util.EnumSet; import java.util.HashSet; import java.util.Properties; -import javax.faces.webapp.FacesServlet; -import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; @@ -22,14 +19,13 @@ import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.request.RequestContextListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; import lcsb.mapviewer.annotation.SpringAnnotationConfig; import lcsb.mapviewer.api.SpringRestApiConfig; import lcsb.mapviewer.persist.SpringPersistConfig; import lcsb.mapviewer.services.SpringServiceConfig; -import lcsb.mapviewer.web.bean.utils.CssContentTypeFilter; +import lcsb.mapviewer.web.bean.utils.ApiAccessControlFilter; import lcsb.mapviewer.web.bean.utils.JsfAjaxAccessControlAllowFilter; import lcsb.mapviewer.web.bean.utils.XFrameAccessControlFilter; @@ -39,18 +35,12 @@ public class WebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) { - /* - * ============ ROOT CONTEXT ============ - */ - AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); - rootContext.setDisplayName("MapViewer2"); rootContext.register( SpringAnnotationConfig.class, SpringPersistConfig.class, SpringRestApiConfig.class, - SpringServiceConfig.class, - SpringWebConfig.class); + SpringServiceConfig.class); container.addListener(new ContextLoaderListener(rootContext)); container.addListener(SessionListener.class); @@ -79,68 +69,15 @@ public class WebAppInitializer implements WebApplicationInitializer { logger.error("Problem with loading log4j configuration: " + file); } - /* - * =========== APP CONTEXT =========== - */ - - AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); - appContext.setParent(rootContext); - - ServletRegistration.Dynamic appDispatcher = container.addServlet("App Servlet", new DispatcherServlet(appContext)); - appDispatcher.setLoadOnStartup(1); - appDispatcher.addMapping( - "*.css", - "*.js", - "*.gif", - "*.jpg", - "*.png", - "*.pdf", - "*.ttf", - "*.svg", - "*.eot", - "*.woff", - "*.woff2", - "/login", - "/j_spring_security_logout"); + AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); + dispatcherContext.setParent(rootContext); + dispatcherContext.register(SpringWebConfig.class); - /* - * ============= FACES CONTEXT ============= - */ - - AnnotationConfigWebApplicationContext facesContext = new AnnotationConfigWebApplicationContext(); - facesContext.setParent(rootContext); - ServletRegistration.Dynamic facesDispatcher = container.addServlet("Faces Servlet", new FacesServlet()); - facesDispatcher.setLoadOnStartup(1); - facesDispatcher.addMapping( - "*.xhtml", - "*.jsf", - "*.faces"); + ServletRegistration.Dynamic dispatcher = + container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext)); + dispatcher.setLoadOnStartup(1); + dispatcher.addMapping("/api/*"); - /* - * ================ REST API CONTEXT ================ - */ - - AnnotationConfigWebApplicationContext restApiContext = new AnnotationConfigWebApplicationContext(); - restApiContext.setParent(rootContext); - restApiContext.register(SpringRestApiConfig.class); - ServletRegistration.Dynamic restApiDispatcher = container.addServlet("REST API Servlet", - new DispatcherServlet(restApiContext)); - restApiDispatcher.setLoadOnStartup(1); - restApiDispatcher.addMapping("/api/*"); - - /* - * ================ WEB CONTEXT ================ - */ - - AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext(); - webContext.setParent(rootContext); - webContext.register(SpringWebConfig.class); - ServletRegistration.Dynamic webDispatcher = container.addServlet("WEB Servlet", - new DispatcherServlet(webContext)); - webDispatcher.setLoadOnStartup(1); - webDispatcher.addMapping("/"); - - /* * ======= FILTERS ======= */ @@ -151,10 +88,6 @@ public class WebAppInitializer implements WebApplicationInitializer { fileUploadFilterReg.setInitParameter("thresholdSize", "51200"); fileUploadFilterReg.addMappingForServletNames(null, true, "Faces Servlet"); - CssContentTypeFilter cssContentTypeFilter = new CssContentTypeFilter(); - FilterRegistration.Dynamic cssContentTypeFilterReg = container.addFilter("cssContentTypeFix", cssContentTypeFilter); - cssContentTypeFilterReg.addMappingForUrlPatterns(null, true, "/*"); - XFrameAccessControlFilter xFrameFilter = new XFrameAccessControlFilter(); FilterRegistration.Dynamic xFrameFilterReg = container.addFilter("xFrameFilter", xFrameFilter); xFrameFilterReg.addMappingForUrlPatterns(null, true, "/*"); @@ -164,11 +97,10 @@ public class WebAppInitializer implements WebApplicationInitializer { jsfAjaxAccessControlAllowFilter); jsfAjaxAccessControlAllowFilterReg.addMappingForUrlPatterns(null, true, "/*"); - DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy(); - FilterRegistration.Dynamic delegatingFilterProxyReg = container.addFilter("springSecurityFilterChain", - delegatingFilterProxy); - delegatingFilterProxyReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.FORWARD, DispatcherType.REQUEST), false, - "/*"); + ApiAccessControlFilter apiFilter = new ApiAccessControlFilter(); + FilterRegistration.Dynamic apiFilterReg = container.addFilter("apiFilter", apiFilter); + apiFilterReg.addMappingForUrlPatterns(null, true, "/api/*"); + /* * =============== COOKIE SETTINGS =============== diff --git a/web/src/main/java/lcsb/mapviewer/web/controler/RootController.java b/web/src/main/java/lcsb/mapviewer/web/controler/RootController.java deleted file mode 100644 index 92959e5ed0c53bd889b3dbd20fda1e262289b956..0000000000000000000000000000000000000000 --- a/web/src/main/java/lcsb/mapviewer/web/controler/RootController.java +++ /dev/null @@ -1,23 +0,0 @@ -package lcsb.mapviewer.web.controler; - -import org.apache.log4j.Logger; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.ModelAndView; - -@Controller -@RequestMapping("/") -public class RootController { - /** - * Default class logger. - */ - private Logger logger = Logger.getLogger(RootController.class); - - @RequestMapping(value = "/", method = { RequestMethod.GET, RequestMethod.POST }) - public ModelAndView rootPage(ModelMap model) { - logger.debug("Accessing root page"); - return new ModelAndView("/index.xhtml", model); - } -} \ No newline at end of file diff --git a/web/src/main/webapp/index.html b/web/src/main/webapp/index.html new file mode 100644 index 0000000000000000000000000000000000000000..f6119f5330ab13d2b333bde34d4402939b50f9d3 --- /dev/null +++ b/web/src/main/webapp/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta http-equiv="refresh" content="0; url=index.xhtml" /> + <p><a href="index.xhtml">Redirect</a></p> +</head> +<body> + +</body> +</html> \ No newline at end of file